jbrowse-plugin-msaview 5.1.0 → 5.1.1
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/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +3 -4
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +22 -17
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +4 -4
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +1 -1
- package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.d.ts +4 -1
- package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +5 -8
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +10 -9
- package/dist/LaunchMsaView/components/OrthologQuery/QuerySpeciesSelect.js +10 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -7
- package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +1 -2
- package/dist/LaunchMsaView/components/SubmitCancelActions.js +2 -2
- package/dist/LaunchMsaView/components/TabPanel.d.ts +1 -1
- package/dist/LaunchMsaView/components/TabPanel.js +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.js +3 -1
- package/dist/LaunchMsaView/components/launchConnectedView.js +8 -1
- package/dist/LaunchMsaView/components/useFeatureSequence.js +6 -4
- package/dist/LaunchMsaView/useQueryRowName.js +6 -2
- package/dist/LaunchMsaView/useQueryRowName.test.js +11 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +3 -13
- package/dist/MsaViewPanel/afterCreateAutoruns.js +57 -57
- package/dist/MsaViewPanel/components/MsaViewPanel.js +6 -6
- package/dist/MsaViewPanel/components/MsaViewPanel.test.js +13 -1
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +2 -6
- package/dist/MsaViewPanel/doLaunchBlast.js +28 -15
- package/dist/MsaViewPanel/doLaunchOrthologs.d.ts +1 -0
- package/dist/MsaViewPanel/doLaunchOrthologs.js +3 -1
- package/dist/MsaViewPanel/fetchIndexedMsa.js +1 -1
- package/dist/MsaViewPanel/loadProteinDomains.d.ts +5 -14
- package/dist/MsaViewPanel/loadProteinDomains.js +6 -20
- package/dist/MsaViewPanel/loadProteinDomains.test.js +1 -2
- package/dist/MsaViewPanel/model.d.ts +21 -21
- package/dist/MsaViewPanel/model.js +0 -3
- package/dist/MsaViewPanel/runLaunch.d.ts +3 -2
- package/dist/MsaViewPanel/runLaunch.js +4 -1
- package/dist/MsaViewPanel/util.d.ts +1 -0
- package/dist/MsaViewPanel/util.js +1 -1
- package/dist/chunks/{AboutDialog-WT6GXMXU.js → AboutDialog-YNPKL64P.js} +1 -1
- package/dist/chunks/{AboutDialog-WT6GXMXU.js.map → AboutDialog-YNPKL64P.js.map} +1 -1
- package/dist/chunks/AnnotationFileDialog-NJGKHAVR.js +2 -0
- package/dist/chunks/{AnnotationFileDialog-34R7LFHB.js.map → AnnotationFileDialog-NJGKHAVR.js.map} +2 -2
- package/dist/chunks/{BgzipFastaMsaAdapter-ZMUARSUQ.js → BgzipFastaMsaAdapter-WWIGI57O.js} +2 -2
- package/dist/chunks/BgzipFastaMsaAdapter-WWIGI57O.js.map +7 -0
- package/dist/chunks/{ExportSVGDialog-NMVUHFGY.js → ExportSVGDialog-D5DKR6CZ.js} +2 -2
- package/dist/chunks/{ExportSVGDialog-NMVUHFGY.js.map → ExportSVGDialog-D5DKR6CZ.js.map} +1 -1
- package/dist/chunks/{FeatureDialog-XNHIA5PS.js → FeatureDialog-7XXL6XUK.js} +1 -1
- package/dist/chunks/{FeatureDialog-XNHIA5PS.js.map → FeatureDialog-7XXL6XUK.js.map} +1 -1
- package/dist/chunks/LaunchMsaViewDialog-C53VHT52.js +5 -0
- package/dist/chunks/LaunchMsaViewDialog-C53VHT52.js.map +7 -0
- package/dist/chunks/MetadataDialog-QCUWTDBU.js +2 -0
- package/dist/chunks/{MetadataDialog-2WT6EX4M.js.map → MetadataDialog-QCUWTDBU.js.map} +2 -2
- package/dist/chunks/MsaViewPanel-DS4STM3W.js +2 -0
- package/dist/chunks/MsaViewPanel-DS4STM3W.js.map +7 -0
- package/dist/chunks/{TrackInfoDialog-TYCMLUKG.js → TrackInfoDialog-BVZWIDEU.js} +2 -2
- package/dist/chunks/{TrackInfoDialog-TYCMLUKG.js.map → TrackInfoDialog-BVZWIDEU.js.map} +1 -1
- package/dist/chunks/TreeNodeInfoDialog-LPLF6BA6.js +2 -0
- package/dist/chunks/{TreeNodeInfoDialog-BFF6JRCO.js.map → TreeNodeInfoDialog-LPLF6BA6.js.map} +2 -2
- package/dist/chunks/chunk-F726IWCW.js +2 -0
- package/dist/chunks/chunk-F726IWCW.js.map +7 -0
- package/dist/chunks/chunk-HOEVB65D.js +2 -0
- package/dist/chunks/chunk-HOEVB65D.js.map +7 -0
- package/dist/chunks/{chunk-5ORPTTMR.js → chunk-IP2BEEIU.js} +1 -1
- package/dist/chunks/{chunk-5ORPTTMR.js.map → chunk-IP2BEEIU.js.map} +1 -1
- package/dist/chunks/{chunk-TITP6LCV.js → chunk-JFL3LJPO.js} +2 -2
- package/dist/chunks/{chunk-TITP6LCV.js.map → chunk-JFL3LJPO.js.map} +2 -2
- package/dist/chunks/chunk-NI3VGKQP.js +3 -0
- package/dist/chunks/chunk-NI3VGKQP.js.map +7 -0
- package/dist/chunks/{chunk-P77DPK6B.js → chunk-NSKY4K4T.js} +1 -1
- package/dist/chunks/{chunk-P77DPK6B.js.map → chunk-NSKY4K4T.js.map} +1 -1
- package/dist/chunks/{chunk-O7JNDIKD.js → chunk-OF4AZJER.js} +1 -1
- package/dist/chunks/{chunk-O7JNDIKD.js.map → chunk-OF4AZJER.js.map} +1 -1
- package/dist/chunks/chunk-P7ZJCOIY.js +11 -0
- package/dist/chunks/chunk-P7ZJCOIY.js.map +7 -0
- package/dist/chunks/{chunk-QMLPITS4.js → chunk-S2T7S4SB.js} +4 -4
- package/dist/chunks/{chunk-QMLPITS4.js.map → chunk-S2T7S4SB.js.map} +2 -2
- package/dist/chunks/{chunk-FCNT6B47.js → chunk-SKDGC3HW.js} +1 -1
- package/dist/chunks/{chunk-FCNT6B47.js.map → chunk-SKDGC3HW.js.map} +1 -1
- package/dist/chunks/{chunk-ENTWPX2K.js → chunk-VJQWGQ4V.js} +5 -5
- package/dist/chunks/{chunk-ENTWPX2K.js.map → chunk-VJQWGQ4V.js.map} +1 -1
- package/dist/chunks/{chunk-ZTQYZDWK.js → chunk-VWM6IHF2.js} +2 -2
- package/dist/chunks/{chunk-ZTQYZDWK.js.map → chunk-VWM6IHF2.js.map} +1 -1
- package/dist/chunks/dist-6DG5HXEW.js +2 -0
- package/dist/chunks/model-DXQOMSLC.js +12 -0
- package/dist/chunks/model-DXQOMSLC.js.map +7 -0
- package/dist/chunks/{renderToSvg-B7RCHRO3.js → renderToSvg-2LBAS6XS.js} +2 -2
- package/dist/jbrowse-plugin-msaview.esm.js +2 -2
- package/dist/jbrowse-plugin-msaview.esm.js.map +1 -1
- package/dist/utils/ebiBlast.d.ts +8 -13
- package/dist/utils/ebiBlast.js +20 -45
- package/dist/utils/ebiJobDispatcher.d.ts +4 -4
- package/dist/utils/ebiJobDispatcher.js +4 -4
- package/dist/utils/eutils.js +1 -1
- package/dist/utils/fetch.d.ts +4 -0
- package/dist/utils/fetch.js +7 -1
- package/dist/utils/homologSearch.d.ts +6 -2
- package/dist/utils/msaRows.d.ts +1 -2
- package/dist/utils/msaRows.js +13 -1
- package/dist/utils/ncbiDomains.d.ts +1 -1
- package/dist/utils/ncbiDomains.js +19 -7
- package/dist/utils/ncbiDomains.test.js +36 -0
- package/dist/utils/ncbiOrthologs.d.ts +24 -5
- package/dist/utils/ncbiOrthologs.js +55 -19
- package/dist/utils/ncbiOrthologs.test.js +58 -8
- package/dist/utils/pantherOrthologs.js +4 -1
- package/dist/utils/phmmer.js +4 -8
- package/dist/utils/taxonomyNames.d.ts +6 -1
- package/dist/utils/taxonomyNames.js +7 -1
- package/dist/utils/taxonomyNames.test.js +12 -0
- package/dist/utils/unirefHomologs.d.ts +6 -0
- package/dist/utils/unirefHomologs.js +25 -10
- package/dist/utils/unirefHomologs.test.js +49 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +17 -17
- package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +23 -20
- package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +4 -4
- package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +3 -1
- package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +7 -19
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +10 -9
- package/src/LaunchMsaView/components/OrthologQuery/QuerySpeciesSelect.tsx +12 -1
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +12 -6
- package/src/LaunchMsaView/components/SubmitCancelActions.tsx +1 -3
- package/src/LaunchMsaView/components/TabPanel.tsx +1 -2
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +3 -1
- package/src/LaunchMsaView/components/launchConnectedView.ts +8 -1
- package/src/LaunchMsaView/components/useFeatureSequence.ts +8 -2
- package/src/LaunchMsaView/useQueryRowName.test.ts +15 -0
- package/src/LaunchMsaView/useQueryRowName.ts +7 -2
- package/src/MsaViewPanel/afterCreateAutoruns.ts +74 -58
- package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +18 -1
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +15 -16
- package/src/MsaViewPanel/doLaunchBlast.ts +37 -17
- package/src/MsaViewPanel/doLaunchOrthologs.ts +4 -1
- package/src/MsaViewPanel/fetchIndexedMsa.ts +1 -1
- package/src/MsaViewPanel/loadProteinDomains.test.ts +1 -2
- package/src/MsaViewPanel/loadProteinDomains.ts +13 -28
- package/src/MsaViewPanel/model.ts +0 -3
- package/src/MsaViewPanel/runLaunch.ts +8 -8
- package/src/MsaViewPanel/types.ts +1 -1
- package/src/MsaViewPanel/util.ts +1 -1
- package/src/utils/ebiBlast.ts +29 -56
- package/src/utils/ebiJobDispatcher.ts +4 -4
- package/src/utils/eutils.ts +1 -1
- package/src/utils/fetch.ts +12 -2
- package/src/utils/homologSearch.ts +6 -2
- package/src/utils/msaRows.ts +18 -4
- package/src/utils/ncbiDomains.test.ts +53 -0
- package/src/utils/ncbiDomains.ts +21 -8
- package/src/utils/ncbiOrthologs.test.ts +70 -7
- package/src/utils/ncbiOrthologs.ts +81 -24
- package/src/utils/pantherOrthologs.ts +7 -1
- package/src/utils/phmmer.ts +3 -7
- package/src/utils/taxonomyNames.test.ts +15 -0
- package/src/utils/taxonomyNames.ts +7 -0
- package/src/utils/unirefHomologs.test.ts +57 -2
- package/src/utils/unirefHomologs.ts +38 -13
- package/src/version.ts +1 -1
- package/dist/LaunchMsaView/components/util.d.ts +0 -7
- package/dist/LaunchMsaView/components/util.js +0 -16
- package/dist/MsaViewPanel/components/ErrorBoundary.d.ts +0 -19
- package/dist/MsaViewPanel/components/ErrorBoundary.js +0 -21
- package/dist/chunks/AnnotationFileDialog-34R7LFHB.js +0 -2
- package/dist/chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js.map +0 -7
- package/dist/chunks/LaunchMsaViewDialog-WBXEEHJL.js +0 -5
- package/dist/chunks/LaunchMsaViewDialog-WBXEEHJL.js.map +0 -7
- package/dist/chunks/MetadataDialog-2WT6EX4M.js +0 -2
- package/dist/chunks/MsaViewPanel-YYP4CYYO.js +0 -2
- package/dist/chunks/MsaViewPanel-YYP4CYYO.js.map +0 -7
- package/dist/chunks/TreeNodeInfoDialog-BFF6JRCO.js +0 -2
- package/dist/chunks/chunk-MBI4TMKR.js +0 -2
- package/dist/chunks/chunk-MBI4TMKR.js.map +0 -7
- package/dist/chunks/chunk-MHBTV7BZ.js +0 -3
- package/dist/chunks/chunk-MHBTV7BZ.js.map +0 -7
- package/dist/chunks/chunk-PJZ3XTMG.js +0 -6
- package/dist/chunks/chunk-PJZ3XTMG.js.map +0 -7
- package/dist/chunks/chunk-QAC6JSDZ.js +0 -5
- package/dist/chunks/chunk-QAC6JSDZ.js.map +0 -7
- package/dist/chunks/chunk-RTKNCGDF.js +0 -8
- package/dist/chunks/chunk-RTKNCGDF.js.map +0 -7
- package/dist/chunks/dist-2T65ZS4E.js +0 -2
- package/dist/chunks/model-7VBTOIJX.js +0 -12
- package/dist/chunks/model-7VBTOIJX.js.map +0 -7
- package/dist/utils/types.d.ts +0 -18
- package/src/LaunchMsaView/components/util.ts +0 -22
- package/src/MsaViewPanel/components/ErrorBoundary.tsx +0 -41
- package/src/utils/types.ts +0 -17
- /package/dist/chunks/{dist-2T65ZS4E.js.map → dist-6DG5HXEW.js.map} +0 -0
- /package/dist/chunks/{renderToSvg-B7RCHRO3.js.map → renderToSvg-2LBAS6XS.js.map} +0 -0
- /package/dist/utils/{types.js → ncbiDomains.test.d.ts} +0 -0
|
@@ -2,12 +2,11 @@ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
|
2
2
|
import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
3
|
export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
4
4
|
configureP: Promise<BaseFeatureDataAdapter> | undefined;
|
|
5
|
-
|
|
5
|
+
msaRowsP: Promise<Map<string, string[]>> | undefined;
|
|
6
6
|
configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
|
|
7
7
|
configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
refNameToMsaId(refName: string): string;
|
|
8
|
+
groupRows(): Promise<Map<string, string[]>>;
|
|
9
|
+
getMSARows(): Promise<Map<string, string[]>>;
|
|
11
10
|
getMSAList(): Promise<string[]>;
|
|
12
11
|
getMSA(id: string): Promise<import("@jbrowse/core/util").Feature[]>;
|
|
13
12
|
}
|
|
@@ -22,30 +22,35 @@ export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
|
22
22
|
});
|
|
23
23
|
return this.configureP;
|
|
24
24
|
}
|
|
25
|
-
async
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
async groupRows() {
|
|
26
|
+
const adapter = await this.configure();
|
|
27
|
+
const separator = new RegExp(this.getConf('msaRegex'));
|
|
28
|
+
const rows = new Map();
|
|
29
|
+
for (const refName of await adapter.getRefNames()) {
|
|
30
|
+
const msaId = refName.split(separator)[0];
|
|
31
|
+
const group = rows.get(msaId);
|
|
32
|
+
if (group) {
|
|
33
|
+
group.push(refName);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
rows.set(msaId, [refName]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return rows;
|
|
40
|
+
}
|
|
41
|
+
getMSARows() {
|
|
42
|
+
this.msaRowsP ??= this.groupRows().catch((e) => {
|
|
43
|
+
this.msaRowsP = undefined;
|
|
30
44
|
throw e;
|
|
31
45
|
});
|
|
32
|
-
return this.
|
|
33
|
-
}
|
|
34
|
-
getMsaRegex() {
|
|
35
|
-
return new RegExp(this.getConf('msaRegex'));
|
|
36
|
-
}
|
|
37
|
-
refNameToMsaId(refName) {
|
|
38
|
-
return refName.split(this.getMsaRegex())[0];
|
|
46
|
+
return this.msaRowsP;
|
|
39
47
|
}
|
|
40
48
|
async getMSAList() {
|
|
41
|
-
|
|
42
|
-
const list = new Set(refNames.map(name => this.refNameToMsaId(name)));
|
|
43
|
-
return [...list];
|
|
49
|
+
return [...(await this.getMSARows()).keys()];
|
|
44
50
|
}
|
|
45
51
|
async getMSA(id) {
|
|
46
52
|
const adapter = await this.configure();
|
|
47
|
-
const
|
|
48
|
-
const rows = refNames.filter(refName => this.refNameToMsaId(refName) === id);
|
|
53
|
+
const rows = (await this.getMSARows()).get(id) ?? [];
|
|
49
54
|
return firstValueFrom(adapter
|
|
50
55
|
.getFeaturesInMultipleRegions(rows.map(refName => ({
|
|
51
56
|
refName,
|
|
@@ -46,14 +46,14 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
46
46
|
const hitCountValid = Number.isInteger(hitCount) && hitCount >= 1;
|
|
47
47
|
const isPhmmer = search.program === 'phmmer';
|
|
48
48
|
const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
49
|
-
const
|
|
49
|
+
const cached = useCachedBlastResults(geneIds);
|
|
50
50
|
const transcriptSelection = useTranscriptSelection({
|
|
51
51
|
feature,
|
|
52
52
|
view,
|
|
53
53
|
preferredTranscriptId,
|
|
54
54
|
});
|
|
55
55
|
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
56
|
-
const e = transcriptSelection.error ?? launchError ??
|
|
56
|
+
const e = transcriptSelection.error ?? launchError ?? cached.error;
|
|
57
57
|
return (React.createElement(React.Fragment, null,
|
|
58
58
|
React.createElement(LaunchPanelContent, { error: e },
|
|
59
59
|
children,
|
|
@@ -80,11 +80,11 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
80
80
|
: 'The hits come back from EBI and are then run through the chosen aligner.',
|
|
81
81
|
' ',
|
|
82
82
|
"The EBI queue is the wait, and it runs from seconds to many minutes."),
|
|
83
|
-
|
|
83
|
+
cached.results.length > 0 ? (React.createElement(Accordion, { className: classes.cachedResultsAccordion },
|
|
84
84
|
React.createElement(AccordionSummary, { expandIcon: React.createElement(ExpandMoreIcon, null) },
|
|
85
85
|
React.createElement(Typography, null, "Previous BLAST Results")),
|
|
86
86
|
React.createElement(AccordionDetails, null,
|
|
87
|
-
React.createElement(CachedBlastResults, { model: model, handleClose: handleClose, feature: feature })))) : null),
|
|
87
|
+
React.createElement(CachedBlastResults, { model: model, handleClose: handleClose, feature: feature, cached: cached })))) : null),
|
|
88
88
|
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !hitCountValid, onSubmit: placement => {
|
|
89
89
|
if (selectedTranscript) {
|
|
90
90
|
const blastParams = search.program === 'phmmer'
|
|
@@ -24,7 +24,7 @@ export default function BlastPanel({ handleClose, model, feature, preferredTrans
|
|
|
24
24
|
const { classes } = useStyles();
|
|
25
25
|
const Panel = panelMap[lookupMethod];
|
|
26
26
|
return (React.createElement(React.Fragment, null,
|
|
27
|
-
React.createElement(Tooltip, { title: `EBI settings — jobs are submitted with ${ebiEmail}` },
|
|
27
|
+
React.createElement(Tooltip, { title: `EBI settings — jobs are submitted with ${ebiEmail.trim() || DEFAULT_EBI_EMAIL}` },
|
|
28
28
|
React.createElement(IconButton, { className: classes.settingsButton, "aria-label": "EBI settings", size: "small", onClick: () => {
|
|
29
29
|
setSettingsOpen(true);
|
|
30
30
|
} },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { CachedBlastResult } from '../../../utils/blastCache';
|
|
3
|
+
import type { useCachedBlastResults } from './useCachedBlastResults';
|
|
3
4
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
4
5
|
/**
|
|
5
6
|
* How the row was produced: `uniprotkb_swissprot / blastp / clustalo`, or
|
|
@@ -12,9 +13,11 @@ import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
|
12
13
|
* queried NCBI directly and blastp/quick-blastp was a real choice.
|
|
13
14
|
*/
|
|
14
15
|
export declare function describeSearch(result: CachedBlastResult): string;
|
|
15
|
-
declare const CachedBlastResults: ({ model, handleClose, feature, }: {
|
|
16
|
+
declare const CachedBlastResults: ({ model, handleClose, feature, cached: { results, handleDelete, handleClearAll }, }: {
|
|
16
17
|
model: AbstractTrackModel;
|
|
17
18
|
handleClose: () => void;
|
|
18
19
|
feature: Feature;
|
|
20
|
+
/** the panel's own listing, so clearing it here empties it there too */
|
|
21
|
+
cached: ReturnType<typeof useCachedBlastResults>;
|
|
19
22
|
}) => React.JSX.Element;
|
|
20
23
|
export default CachedBlastResults;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { ErrorMessage } from '@jbrowse/core/ui';
|
|
3
3
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
4
4
|
import { Button, IconButton, List, ListItem, ListItemButton, ListItemText, Typography, } from '@mui/material';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
6
|
import { makeStyles } from 'tss-react/mui';
|
|
7
|
-
import { featureMatchesId,
|
|
7
|
+
import { featureMatchesId, getLinearGenomeView, getSortedTranscriptFeatures, } from '../../util';
|
|
8
8
|
import { builtAlignmentLook, launchConnectedView } from '../launchConnectedView';
|
|
9
9
|
import { useLaunchPlacement } from '../launchPlacement';
|
|
10
|
-
import { useCachedBlastResults } from './useCachedBlastResults';
|
|
11
10
|
const useStyles = makeStyles()({
|
|
12
11
|
header: {
|
|
13
12
|
display: 'flex',
|
|
@@ -50,13 +49,11 @@ export function describeSearch(result) {
|
|
|
50
49
|
.filter(Boolean)
|
|
51
50
|
.join(' / ');
|
|
52
51
|
}
|
|
53
|
-
const CachedBlastResults = observer(function ({ model, handleClose, feature, }) {
|
|
52
|
+
const CachedBlastResults = observer(function ({ model, handleClose, feature, cached: { results, handleDelete, handleClearAll }, }) {
|
|
54
53
|
const { classes } = useStyles();
|
|
55
54
|
const view = getLinearGenomeView(model);
|
|
56
55
|
const [operationError, setOperationError] = useState();
|
|
57
56
|
const [sideBySide] = useLaunchPlacement();
|
|
58
|
-
const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
59
|
-
const { results, error, isLoading, handleDelete, handleClearAll } = useCachedBlastResults(geneIds);
|
|
60
57
|
const handleUseCached = (cached) => {
|
|
61
58
|
// the cached query row is the plugin's default `QUERY`, translated from
|
|
62
59
|
// the transcript stored as transcriptId, so that transcript relinks it
|
|
@@ -75,8 +72,8 @@ const CachedBlastResults = observer(function ({ model, handleClose, feature, })
|
|
|
75
72
|
setOperationError(e);
|
|
76
73
|
});
|
|
77
74
|
};
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
return (React.createElement("div", null,
|
|
76
|
+
operationError ? React.createElement(ErrorMessage, { error: operationError }) : null,
|
|
80
77
|
React.createElement("div", { className: classes.header },
|
|
81
78
|
React.createElement(Typography, { variant: "subtitle1" },
|
|
82
79
|
"Cached BLAST Results (",
|
|
@@ -22,13 +22,14 @@ import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
|
22
22
|
*/
|
|
23
23
|
function useMsaFileText(location) {
|
|
24
24
|
const debounced = useDebounced(location, 500);
|
|
25
|
-
const { data } = useFetch(debounced ? [JSON.stringify(debounced), 'msa-file-text'] : null, () => openLocation(debounced).readFile('utf8'));
|
|
26
|
-
return
|
|
25
|
+
const { data, error, isLoading } = useFetch(debounced ? [JSON.stringify(debounced), 'msa-file-text'] : null, () => openLocation(debounced).readFile('utf8'));
|
|
26
|
+
return {
|
|
27
|
+
text: data ?? '',
|
|
28
|
+
error,
|
|
29
|
+
isLoading: isLoading || debounced !== location,
|
|
30
|
+
};
|
|
27
31
|
}
|
|
28
32
|
const useStyles = makeStyles()({
|
|
29
|
-
textAreaFont: {
|
|
30
|
-
fontFamily: 'Courier New',
|
|
31
|
-
},
|
|
32
33
|
inputContainer: {
|
|
33
34
|
marginBottom: 30,
|
|
34
35
|
},
|
|
@@ -54,9 +55,9 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
54
55
|
preferredTranscriptId,
|
|
55
56
|
});
|
|
56
57
|
const { selectedTranscript, proteinSequence, error, sequenceStatus } = transcriptSelection;
|
|
57
|
-
const
|
|
58
|
-
const queryRow = useQueryRowName(inputMethod === 'file' ?
|
|
59
|
-
const e = launchError ?? error;
|
|
58
|
+
const msaFile = useMsaFileText(inputMethod === 'file' ? msaFileLocation : undefined);
|
|
59
|
+
const queryRow = useQueryRowName(inputMethod === 'file' ? msaFile.text : msaText, proteinSequence);
|
|
60
|
+
const e = launchError ?? error ?? msaFile.error;
|
|
60
61
|
return (React.createElement(React.Fragment, null,
|
|
61
62
|
React.createElement(LaunchPanelContent, { error: e },
|
|
62
63
|
React.createElement(FormControl, { component: "fieldset" },
|
|
@@ -77,7 +78,7 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
77
78
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
78
79
|
React.createElement(QueryRowSelector, { ...queryRow })),
|
|
79
80
|
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !selectedTranscript ||
|
|
80
|
-
(inputMethod === 'file' && !msaFileLocation) ||
|
|
81
|
+
(inputMethod === 'file' && (!msaFileLocation || msaFile.isLoading)) ||
|
|
81
82
|
(inputMethod === 'text' && !msaText.trim()), onSubmit: placement => {
|
|
82
83
|
if (selectedTranscript) {
|
|
83
84
|
submit(() => launchConnectedView({
|
|
@@ -8,7 +8,16 @@ async function describeTaxon(query, signal) {
|
|
|
8
8
|
if (!taxId) {
|
|
9
9
|
throw new Error(`No NCBI taxon matches "${query}"`);
|
|
10
10
|
}
|
|
11
|
-
const
|
|
11
|
+
const failures = [];
|
|
12
|
+
const info = (await fetchTaxonomyInfo([taxId], signal, e => {
|
|
13
|
+
failures.push(e);
|
|
14
|
+
})).get(taxId);
|
|
15
|
+
// a throttle would otherwise read as a taxon NCBI has no name for
|
|
16
|
+
if (!info && failures.length > 0) {
|
|
17
|
+
throw new Error(`NCBI did not answer for the name of taxon ${taxId}`, {
|
|
18
|
+
cause: failures[0],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
12
21
|
const label = [info?.sciname, info?.commonName && `(${info.commonName})`]
|
|
13
22
|
.filter(Boolean)
|
|
14
23
|
.join(' ');
|
|
@@ -7,7 +7,7 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
7
7
|
import TextField2 from '../../../components/TextField2';
|
|
8
8
|
import { useFetch } from '../../../utils/useFetch';
|
|
9
9
|
import { resolveQueryRowName, useQueryRowName } from '../../useQueryRowName';
|
|
10
|
-
import { getGeneDisplayName, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
|
|
10
|
+
import { featureMatchesId, getGeneDisplayName, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
|
|
11
11
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
12
12
|
import QueryRowSelector from '../QueryRowSelector';
|
|
13
13
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
@@ -39,11 +39,16 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
39
39
|
validIds: msaList,
|
|
40
40
|
preferredTranscriptId,
|
|
41
41
|
});
|
|
42
|
-
const {
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
43
|
+
// the dataset's name for this transcript, which a track is free not to use
|
|
44
|
+
// as the feature's own id
|
|
45
|
+
const msaId = selectedTranscript
|
|
46
|
+
? msaList?.find(id => featureMatchesId(selectedTranscript, id))
|
|
47
|
+
: undefined;
|
|
48
|
+
const { data: msaData, isLoading: msaDataLoading, error: msaDataFetchError, } = useFetch(msaId && selectedDataset
|
|
49
|
+
? `${selectedDataset.datasetId}-${msaId}-msa`
|
|
45
50
|
: null, () => fetchMSA({
|
|
46
|
-
msaId:
|
|
51
|
+
msaId: msaId,
|
|
47
52
|
config: selectedDataset.adapter,
|
|
48
53
|
pluginManager,
|
|
49
54
|
}));
|
|
@@ -59,7 +64,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
59
64
|
// that convention does carry the row, so it is worth falling back to when the
|
|
60
65
|
// residues do not match closely enough to find it -- but only when the
|
|
61
66
|
// alignment really has it, which is the check the old code never made.
|
|
62
|
-
const querySeqName = resolveQueryRowName(queryRow, `${
|
|
67
|
+
const querySeqName = resolveQueryRowName(queryRow, `${msaId ?? ''}_${assemblyNames[0] ?? ''}`);
|
|
63
68
|
const e = msaListFetchError ??
|
|
64
69
|
msaDataFetchError ??
|
|
65
70
|
transcriptSelection.error ??
|
|
@@ -70,7 +75,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
70
75
|
setSelectedDatasetId(event.target.value);
|
|
71
76
|
} }, datasets?.map(d => (React.createElement(MenuItem, { key: d.datasetId, value: d.datasetId }, d.name)))),
|
|
72
77
|
selectedDataset ? (React.createElement("div", { className: classes.selectedContainer },
|
|
73
|
-
!msaListLoading && msaDataLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading MSA for ${getTranscriptDisplayName(selectedTranscript) ||
|
|
78
|
+
!msaListLoading && msaDataLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading MSA for ${getTranscriptDisplayName(selectedTranscript) || msaId}` })) : null,
|
|
74
79
|
msaListLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading available MSAs for (${selectedDataset.name})` })) : null,
|
|
75
80
|
msaList ? (React.createElement("div", null,
|
|
76
81
|
React.createElement(SanitizedHTML, { html: selectedDataset.description }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { MsaViewPlacement } from '../../utils/workspaces';
|
|
3
3
|
import type { AbstractTrackModel } from '@jbrowse/core/util';
|
|
4
|
-
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel,
|
|
4
|
+
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel, model, }: {
|
|
5
5
|
/** handed the placement the box states, so no launch re-reads storage */
|
|
6
6
|
onSubmit: (placement: MsaViewPlacement) => void;
|
|
7
7
|
onCancel: () => void;
|
|
@@ -9,7 +9,6 @@ export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled
|
|
|
9
9
|
/** why Submit is grey, shown beside it */
|
|
10
10
|
hint?: React.ReactNode;
|
|
11
11
|
submitLabel?: string;
|
|
12
|
-
cancelLabel?: string;
|
|
13
12
|
/** omitted by a panel that submits something other than a view launch */
|
|
14
13
|
model?: AbstractTrackModel;
|
|
15
14
|
}): React.JSX.Element;
|
|
@@ -19,7 +19,7 @@ function PlacementToggle({ checked, onChange, }) {
|
|
|
19
19
|
onChange(event.target.checked);
|
|
20
20
|
} }) }));
|
|
21
21
|
}
|
|
22
|
-
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel = 'Submit',
|
|
22
|
+
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel = 'Submit', model, }) {
|
|
23
23
|
const [sideBySide, setSideBySide] = useLaunchPlacement();
|
|
24
24
|
// The stored value is what the next dialog opens on, so it is written on
|
|
25
25
|
// submit rather than on the click: a dialog the user backed out of must not
|
|
@@ -46,5 +46,5 @@ export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled
|
|
|
46
46
|
} }, submitLabel),
|
|
47
47
|
React.createElement(Button, { sx: { flexShrink: 0 }, color: "secondary", variant: "contained", onClick: () => {
|
|
48
48
|
onCancel();
|
|
49
|
-
} },
|
|
49
|
+
} }, "Cancel"))));
|
|
50
50
|
}
|
|
@@ -7,11 +7,11 @@ import React, { useState } from 'react';
|
|
|
7
7
|
// Lazy on first visit rather than mounted up front, because a panel mounts
|
|
8
8
|
// fetches of its own: rendering all four on open would query the MSA dataset
|
|
9
9
|
// adapter and the BLAST cache for tabs nobody looked at.
|
|
10
|
-
export default function TabPanel({ children, value, index,
|
|
10
|
+
export default function TabPanel({ children, value, index, }) {
|
|
11
11
|
const active = value === index;
|
|
12
12
|
const [visited, setVisited] = useState(active);
|
|
13
13
|
if (active && !visited) {
|
|
14
14
|
setVisited(true);
|
|
15
15
|
}
|
|
16
|
-
return (React.createElement("div", { role: "tabpanel", hidden: !active
|
|
16
|
+
return (React.createElement("div", { role: "tabpanel", hidden: !active }, visited || active ? children : null));
|
|
17
17
|
}
|
|
@@ -50,5 +50,7 @@ export default function TranscriptSelector({ feature, options, selectedId, selec
|
|
|
50
50
|
? `>${getTranscriptDisplayName(selectedTranscript)}\n${proteinSequence}`
|
|
51
51
|
: sequenceStatus === 'missing'
|
|
52
52
|
? 'This transcript has no coding sequence, so there is nothing to translate.'
|
|
53
|
-
: '
|
|
53
|
+
: sequenceStatus === 'error'
|
|
54
|
+
? 'The sequence could not be fetched.'
|
|
55
|
+
: 'Loading...' })) : null));
|
|
54
56
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useRef, useState } from 'react';
|
|
2
2
|
import { getSession } from '@jbrowse/core/util';
|
|
3
3
|
import { launchMsaView } from '../../utils/launchMsaView';
|
|
4
4
|
/** how a view whose alignment the plugin builds opens */
|
|
@@ -22,11 +22,18 @@ export function launchConnectedView({ view, feature, placement, ...snapshot }) {
|
|
|
22
22
|
/** runs a panel's launch, closing the dialog on success and keeping the error */
|
|
23
23
|
export function useLaunchSubmit(handleClose) {
|
|
24
24
|
const [launchError, setLaunchError] = useState();
|
|
25
|
+
// the first launch loads the view's chunk, which is long enough to click twice
|
|
26
|
+
const launching = useRef(false);
|
|
25
27
|
return {
|
|
26
28
|
launchError,
|
|
27
29
|
submit: (launch) => {
|
|
30
|
+
if (launching.current) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
launching.current = true;
|
|
28
34
|
setLaunchError(undefined);
|
|
29
35
|
launch().then(handleClose, (e) => {
|
|
36
|
+
launching.current = false;
|
|
30
37
|
console.error(e);
|
|
31
38
|
setLaunchError(e);
|
|
32
39
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
1
2
|
import { getSession } from '@jbrowse/core/util';
|
|
2
3
|
import { translateTranscript } from '@jbrowse/core/util/translateTranscript';
|
|
3
4
|
import { useFetch } from '../../utils/useFetch';
|
|
@@ -16,11 +17,12 @@ export function useFeatureSequence({ view, feature, }) {
|
|
|
16
17
|
session: getSession(view),
|
|
17
18
|
});
|
|
18
19
|
});
|
|
20
|
+
const proteinSequence = useMemo(() => sequence && feature
|
|
21
|
+
? (translateTranscript({ transcript: feature, ...sequence })?.protein ??
|
|
22
|
+
'')
|
|
23
|
+
: '', [sequence, feature]);
|
|
19
24
|
return {
|
|
20
|
-
proteinSequence
|
|
21
|
-
? (translateTranscript({ transcript: feature, ...sequence })?.protein ??
|
|
22
|
-
'')
|
|
23
|
-
: '',
|
|
25
|
+
proteinSequence,
|
|
24
26
|
sequence,
|
|
25
27
|
error,
|
|
26
28
|
isLoading,
|
|
@@ -24,7 +24,11 @@ export function useQueryRowName(msaText, proteinSequence) {
|
|
|
24
24
|
// parsing runs on every keystroke in the paste box otherwise, and an
|
|
25
25
|
// alignment of a few hundred rows is not free
|
|
26
26
|
const { names, match } = useMemo(() => findQueryRow(msaText, proteinSequence), [msaText, proteinSequence]);
|
|
27
|
-
|
|
27
|
+
// a row picked from one alignment says nothing about the next one pasted
|
|
28
|
+
const picked = override !== undefined && (names.length === 0 || names.includes(override))
|
|
29
|
+
? override
|
|
30
|
+
: undefined;
|
|
31
|
+
const querySeqName = picked ?? match?.name ?? '';
|
|
28
32
|
return {
|
|
29
33
|
detected: match,
|
|
30
34
|
names,
|
|
@@ -33,6 +37,6 @@ export function useQueryRowName(msaText, proteinSequence) {
|
|
|
33
37
|
// taken at face value, residue 1 to residue 1
|
|
34
38
|
querySeqOffset: querySeqName === match?.name ? match.offset : 0,
|
|
35
39
|
setQuerySeqName: setOverride,
|
|
36
|
-
isAutoDetected:
|
|
40
|
+
isAutoDetected: picked === undefined && !!match,
|
|
37
41
|
};
|
|
38
42
|
}
|
|
@@ -20,6 +20,17 @@ describe('useQueryRowName', () => {
|
|
|
20
20
|
expect(result.current.querySeqName).toBe('other');
|
|
21
21
|
expect(result.current.querySeqOffset).toBe(0);
|
|
22
22
|
});
|
|
23
|
+
// the pick outlived the alignment it was made in, and the launch then named
|
|
24
|
+
// a row the new one does not have
|
|
25
|
+
test('a picked row is dropped once the alignment no longer has it', () => {
|
|
26
|
+
const { result, rerender } = renderHook(({ msa }) => useQueryRowName(msa, protein), { initialProps: { msa: trimmed } });
|
|
27
|
+
act(() => {
|
|
28
|
+
result.current.setQuerySeqName('other');
|
|
29
|
+
});
|
|
30
|
+
rerender({ msa: trimmed.replace('>other', '>renamed') });
|
|
31
|
+
expect(result.current.querySeqName).toBe('aligned_query');
|
|
32
|
+
expect(result.current.isAutoDetected).toBe(true);
|
|
33
|
+
});
|
|
23
34
|
});
|
|
24
35
|
// a pre-loaded dataset names its rows by a convention of its own, and the
|
|
25
36
|
// residues can be too diverged for detection to find the row that is right
|
|
@@ -8,22 +8,12 @@ export declare function loadStoredData(self: JBrowsePluginMsaViewModel): void;
|
|
|
8
8
|
* refusing IndexedDB fails once rather than in a loop.
|
|
9
9
|
*/
|
|
10
10
|
export declare function storeDataToIndexedDB(self: JBrowsePluginMsaViewModel): void;
|
|
11
|
-
/**
|
|
12
|
-
* Same shape as launchBlastIfNeeded, for the ortholog path: the params ARE the
|
|
13
|
-
* request, and `launchCompleted` is what marks it done. They are left in place
|
|
14
|
-
* either way -- on failure so the error stays attributable to a specific
|
|
15
|
-
* request, and on success because they are the only durable statement of what
|
|
16
|
-
* the view is, which is what a stored alignment that expired is rebuilt from.
|
|
17
|
-
* The autorun tracks those two reads alone, so nothing refires until a new
|
|
18
|
-
* request replaces them or a retry clears the mark.
|
|
19
|
-
*/
|
|
20
11
|
export declare function launchOrthologsIfNeeded(self: JBrowsePluginMsaViewModel): void;
|
|
21
12
|
export declare function launchBlastIfNeeded(self: JBrowsePluginMsaViewModel): void;
|
|
22
13
|
/**
|
|
23
|
-
* Once an
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* returns no domains (which leaves the annotation list empty).
|
|
14
|
+
* Once an alignment whose rows carry accessions is present, fetch NCBI CDD
|
|
15
|
+
* domains for them and overlay them. `domainsRequested` makes this once per
|
|
16
|
+
* view, so NCBI returning no domains does not refire it.
|
|
27
17
|
*/
|
|
28
18
|
export declare function autoLoadProteinDomains(self: JBrowsePluginMsaViewModel): void;
|
|
29
19
|
export declare function processInit(self: JBrowsePluginMsaViewModel): void;
|