jbrowse-plugin-msaview 3.6.0 → 3.7.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/README.md +8 -0
- package/dist/LaunchMsaView/cleanProteinSequence.test.d.ts +1 -0
- package/dist/LaunchMsaView/cleanProteinSequence.test.js +77 -0
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +29 -32
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +10 -15
- package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +14 -13
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +25 -22
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +14 -19
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.d.ts +2 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +18 -2
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.js +13 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +19 -14
- package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +3 -1
- package/dist/LaunchMsaView/components/SubmitCancelActions.js +10 -7
- package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +20 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +8 -1
- package/dist/LaunchMsaView/components/calculateProteinSequence.js +25 -20
- package/dist/LaunchMsaView/components/launchConnectedView.d.ts +23 -0
- package/dist/LaunchMsaView/components/launchConnectedView.js +39 -0
- package/dist/LaunchMsaView/detectQueryRow.js +4 -7
- package/dist/LaunchMsaView/util.d.ts +6 -0
- package/dist/LaunchMsaView/util.js +7 -1
- package/dist/LaunchMsaViewExtensionPoint/index.test.js +15 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.js +12 -3
- package/dist/MsaViewPanel/applyRegion.d.ts +23 -0
- package/dist/MsaViewPanel/applyRegion.js +19 -0
- package/dist/MsaViewPanel/applyRegion.test.d.ts +1 -0
- package/dist/MsaViewPanel/applyRegion.test.js +45 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +2 -1
- package/dist/MsaViewPanel/doLaunchBlast.test.js +12 -0
- package/dist/MsaViewPanel/doLaunchOrthologs.test.js +1 -1
- package/dist/MsaViewPanel/genomeToMSA.js +3 -3
- package/dist/MsaViewPanel/genomeToMSA.test.js +69 -23
- package/dist/MsaViewPanel/launchAutoruns.test.d.ts +1 -0
- package/dist/MsaViewPanel/launchAutoruns.test.js +36 -0
- package/dist/MsaViewPanel/model.d.ts +18 -14
- package/dist/MsaViewPanel/model.js +17 -0
- package/dist/MsaViewPanel/msaDataStore.d.ts +5 -3
- package/dist/MsaViewPanel/msaDataStore.js +3 -12
- package/dist/MsaViewPanel/msaDataStore.test.js +8 -1
- package/dist/MsaViewPanel/observeProteinHighlights.test.js +4 -4
- package/dist/MsaViewPanel/regionSnapshot.test.d.ts +1 -0
- package/dist/MsaViewPanel/regionSnapshot.test.js +29 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +41 -4
- package/dist/MsaViewPanel/resolveConnectedTranscript.js +103 -68
- package/dist/MsaViewPanel/resolveConnectedTranscript.test.d.ts +1 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +149 -0
- package/dist/MsaViewPanel/runLaunch.d.ts +8 -4
- package/dist/MsaViewPanel/runLaunch.js +15 -4
- package/dist/MsaViewPanel/runLaunch.test.js +16 -0
- package/dist/MsaViewPanel/storedData.test.js +23 -0
- package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +3 -2
- package/dist/MsaViewPanel/util.d.ts +23 -23
- package/dist/MsaViewPanel/util.js +23 -24
- package/dist/index.js +17 -10
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +36 -36
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/blastCache.d.ts +26 -2
- package/dist/utils/blastCache.js +51 -39
- package/dist/utils/blastCache.test.js +46 -19
- package/dist/utils/domainCache.d.ts +1 -1
- package/dist/utils/domainCache.js +18 -14
- package/dist/utils/ebiJobDispatcher.d.ts +11 -0
- package/dist/utils/ebiJobDispatcher.js +12 -13
- package/dist/utils/ebiJobDispatcher.test.js +17 -3
- package/dist/utils/eutils.d.ts +14 -0
- package/dist/utils/eutils.js +48 -14
- package/dist/utils/eutils.test.d.ts +1 -0
- package/dist/utils/eutils.test.js +31 -0
- package/dist/utils/idb.d.ts +16 -3
- package/dist/utils/idb.js +51 -5
- package/dist/utils/idb.test.d.ts +1 -0
- package/dist/utils/idb.test.js +78 -0
- package/dist/utils/ncbiDomains.js +4 -4
- package/dist/utils/ncbiOrthologs.d.ts +1 -1
- package/dist/utils/ncbiOrthologs.js +18 -17
- package/dist/utils/ncbiOrthologs.test.js +17 -1
- package/dist/utils/ncbiTaxonomy.js +14 -15
- package/dist/utils/taxonomyNames.js +23 -19
- package/dist/utils/taxonomyNames.test.d.ts +1 -0
- package/dist/utils/taxonomyNames.test.js +33 -0
- package/dist/utils/unirefHomologs.js +2 -2
- package/dist/utils/unirefHomologs.test.js +8 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -9
- package/src/LaunchMsaView/cleanProteinSequence.test.ts +91 -0
- package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +35 -32
- package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +10 -14
- package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +16 -15
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +34 -21
- package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +18 -21
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.ts +16 -0
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +24 -2
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +19 -13
- package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +27 -2
- package/src/LaunchMsaView/components/SubmitCancelActions.tsx +12 -7
- package/src/LaunchMsaView/components/calculateProteinSequence.ts +28 -18
- package/src/LaunchMsaView/components/launchConnectedView.ts +56 -0
- package/src/LaunchMsaView/detectQueryRow.ts +5 -7
- package/src/LaunchMsaView/util.ts +7 -1
- package/src/LaunchMsaViewExtensionPoint/index.test.ts +16 -0
- package/src/LaunchMsaViewExtensionPoint/index.ts +28 -1
- package/src/MsaViewPanel/afterCreateAutoruns.ts +12 -3
- package/src/MsaViewPanel/applyRegion.test.ts +55 -0
- package/src/MsaViewPanel/applyRegion.ts +33 -0
- package/src/MsaViewPanel/doLaunchBlast.test.ts +20 -1
- package/src/MsaViewPanel/doLaunchBlast.ts +2 -1
- package/src/MsaViewPanel/doLaunchOrthologs.test.ts +1 -1
- package/src/MsaViewPanel/genomeToMSA.test.ts +75 -23
- package/src/MsaViewPanel/genomeToMSA.ts +3 -2
- package/src/MsaViewPanel/launchAutoruns.test.ts +45 -0
- package/src/MsaViewPanel/model.ts +22 -0
- package/src/MsaViewPanel/msaDataStore.test.ts +14 -2
- package/src/MsaViewPanel/msaDataStore.ts +4 -12
- package/src/MsaViewPanel/observeProteinHighlights.test.ts +4 -4
- package/src/MsaViewPanel/regionSnapshot.test.ts +34 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +183 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.ts +154 -71
- package/src/MsaViewPanel/runLaunch.test.ts +19 -0
- package/src/MsaViewPanel/runLaunch.ts +21 -4
- package/src/MsaViewPanel/storedData.test.ts +32 -0
- package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +3 -2
- package/src/MsaViewPanel/util.ts +38 -29
- package/src/index.ts +19 -10
- package/src/utils/blastCache.test.ts +59 -20
- package/src/utils/blastCache.ts +62 -39
- package/src/utils/domainCache.ts +27 -15
- package/src/utils/ebiJobDispatcher.test.ts +18 -3
- package/src/utils/ebiJobDispatcher.ts +12 -16
- package/src/utils/eutils.test.ts +39 -0
- package/src/utils/eutils.ts +64 -14
- package/src/utils/idb.test.ts +106 -0
- package/src/utils/idb.ts +57 -5
- package/src/utils/ncbiDomains.ts +4 -4
- package/src/utils/ncbiOrthologs.test.ts +20 -0
- package/src/utils/ncbiOrthologs.ts +26 -28
- package/src/utils/ncbiTaxonomy.ts +17 -20
- package/src/utils/taxonomyNames.test.ts +41 -0
- package/src/utils/taxonomyNames.ts +33 -19
- package/src/utils/unirefHomologs.test.ts +11 -1
- package/src/utils/unirefHomologs.ts +2 -2
- package/src/version.ts +1 -1
- package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.d.ts +0 -16
- package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.js +0 -31
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +0 -16
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +0 -16
- package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.d.ts +0 -9
- package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.js +0 -15
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +0 -13
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +0 -22
- package/src/LaunchMsaView/components/BlastQuery/blastLaunchView.ts +0 -59
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +0 -43
- package/src/LaunchMsaView/components/OrthologQuery/orthologLaunchView.ts +0 -31
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +0 -42
package/README.md
CHANGED
|
@@ -14,6 +14,14 @@ MSAView plugin running in JBrowse 2
|
|
|
14
14
|
|
|
15
15
|
## Demo
|
|
16
16
|
|
|
17
|
+
[p53 across vertebrates, linked to the genome and the structure](https://jbrowse.org/code/jb2/main/?config=https://jbrowse.org/ucsc/hg38/config.json&session=spec-{%22views%22:[{%22type%22:%22LinearGenomeView%22,%22id%22:%22lgv1%22,%22assembly%22:%22hg38%22,%22loc%22:%22chr17:7,661,779-7,687,538%22,%22tracks%22:[%22hg38-ncbiRefSeqSelect%22,%22hg38-clinvarMain%22]},{%22type%22:%22MsaView%22,%22displayName%22:%22p53%20across%20vertebrates%22,%22msaFileLocation%22:{%22uri%22:%22https://gmod.org/JBrowseMSA/demo/data/p53/p53-vertebrates.afa%22},%22treeFileLocation%22:{%22uri%22:%22https://gmod.org/JBrowseMSA/demo/data/p53/p53-vertebrates.nh%22},%22querySeqName%22:%22Human%22,%22relativeTo%22:%22Human%22,%22connectedViewId%22:%22lgv1%22,%22connectedTranscript%22:%22NM_000546.6%22,%22placement%22:%22splitRight%22,%22colorSchemeName%22:%22clustalx_protein_dynamic%22,%22highlights%22:[{%22row%22:%22Human%22,%22start%22:102,%22end%22:292,%22label%22:%22DNA-binding%22,%22color%22:%22rgba%28255,140,0,0.15%29%22},{%22row%22:%22Human%22,%22start%22:175,%22end%22:175,%22label%22:%22R175%22},{%22row%22:%22Human%22,%22start%22:245,%22end%22:245,%22label%22:%22G245%22},{%22row%22:%22Human%22,%22start%22:248,%22end%22:248,%22label%22:%22R248%22},{%22row%22:%22Human%22,%22start%22:249,%22end%22:249,%22label%22:%22R249%22},{%22row%22:%22Human%22,%22start%22:273,%22end%22:273,%22label%22:%22R273%22},{%22row%22:%22Human%22,%22start%22:282,%22end%22:282,%22label%22:%22R282%22}],%22columnTracks%22:[{%22id%22:%22clinvar%22,%22name%22:%22ClinVar%20pathogenic%20missense%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%23c0392b%22,%22height%22:60,%22max%22:8,%22values%22:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,2,4,3,0,6,0,0,0,0,0,0,0,0,0,0,1,2,1,5,0,0,2,2,3,1,1,4,0,0,2,0,0,2,0,3,0,0,0,1,0,0,0,6,2,0,0,1,1,3,6,2,0,1,0,2,1,1,0,0,2,0,0,2,1,3,0,3,3,1,3,5,1,3,0,0,0,0,0,0,0,0,1,0,0,4,3,1,1,1,0,1,0,0,0,0,0,3,0,0,0,0,0,0,0,3,2,1,1,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,3,0,4,0,2,4,6,2,3,5,2,0,4,5,4,1,6,2,1,2,0,1,3,0,0,0,2,1,0,0,0,0,0,2,3,3,0,1,4,1,3,6,0,2,1,1,3,0,3,8,3,1,0,2,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,5,0,0,0,1,1,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},{%22id%22:%22alphamissense%22,%22name%22:%22AlphaMissense%20mean%20%28x100%29%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%231565c0%22,%22height%22:60,%22max%22:100,%22values%22:[49,27,19,11,12,14,18,10,12,14,28,17,40,57,73,65,65,68,95,29,30,72,95,21,24,60,39,21,18,17,18,16,17,12,11,13,14,13,14,21,22,20,17,20,16,16,15,14,17,17,15,13,37,29,11,19,16,11,11,12,14,20,14,12,11,20,13,19,15,14,12,13,15,17,14,17,15,19,20,17,15,15,22,22,19,22,20,24,17,23,67,41,46,66,63,37,90,98,63,35,36,20,76,15,99,13,58,48,98,26,81,57,97,43,17,64,87,68,90,98,96,92,73,81,98,91,95,23,13,81,56,97,84,97,98,87,86,81,91,84,94,93,89,66,81,57,77,26,20,13,90,69,17,52,70,49,91,98,87,88,94,87,95,89,48,54,52,76,76,68,92,87,98,78,99,100,98,97,100,96,85,50,38,52,32,74,52,32,83,87,71,47,99,95,89,95,91,96,95,81,20,25,69,48,98,24,64,93,17,23,84,56,99,92,91,93,70,89,76,91,77,27,92,87,57,92,58,35,54,68,75,87,85,87,74,93,96,100,95,98,99,100,94,100,100,98,97,100,99,93,93,59,91,91,87,92,93,99,87,22,21,94,24,76,83,99,98,36,61,97,93,93,99,95,100,97,100,100,99,100,100,97,84,60,98,96,47,62,13,18,56,34,17,17,11,9,9,16,11,15,13,18,20,17,81,75,26,26,23,13,12,14,16,18,18,14,15,15,47,54,46,19,13,34,24,50,56,88,36,87,48,93,63,98,73,40,89,90,45,60,88,51,36,81,77,55,79,75,87,31,54,67,23,18,17,16,25,22,13,15,15,16,20,18,15,20,25,13,15,56,21,47,45,21,16,16,15,21,16,20,66,47,13,26,19,66,13,28,18,16,47,54,66]},{%22id%22:%22mavedb%22,%22name%22:%22MaveDB%20nutlin-3,%20p53WT%22,%22kind%22:%22bar%22,%22row%22:%22Human%22,%22color%22:%22%232e7d32%22,%22height%22:60,%22max%22:2,%22values%22:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,0,0,0.2,0.4,0,0,0,0,0.1,0,0.8,0,0.7,0.3,1.6,0.1,1.4,0.1,1.3,0,0,0,0.1,0,0.1,0.5,0.2,0.4,0.4,0.8,0.8,1.4,1.5,0.1,0.1,1.2,1,1.7,1.1,1.4,1.3,0.8,0.5,0.5,0.8,0.4,1.2,0.4,1.4,0.7,1.2,0.6,1.1,0.5,0.4,0.6,1.5,1.3,0,0.7,1.3,0.3,1.4,1,1.4,0.8,1.4,0.8,1.8,0.8,0.3,0,0.2,0.8,0.4,0,0.6,0.7,1.7,1,1.2,2,1.1,0.6,2,0.8,0.7,0.3,0.1,0.4,0.2,0.3,0.1,0.1,0.5,0.8,0.1,0.1,1.4,1.6,1.4,0.7,1.3,0.1,0.1,0,0,0,0.9,0.3,1.8,0,0,1,0,0.1,0.9,0.7,1.2,1.1,1.5,1.4,0.6,1.3,0.5,1.5,0.4,0.3,0.6,0.3,0.1,0.4,0.3,0.2,0.7,0.5,0.6,1.4,0.5,1.4,0.6,1.7,1.1,1.9,0.8,1.3,1.3,1.7,0.9,1.7,2.2,2,0.9,1.5,2.1,1,1.4,0.3,1.3,1.2,1.4,0.8,1.4,1.4,0.9,0.5,0.5,0.7,0.5,0.5,0.8,1.6,1,0.9,0.8,1.5,1.3,1.5,1.5,1.7,1.8,0.5,0.4,2,0,0.9,0.6,1,0,0,1,1.8,0,0.1,0.2,0.1,0.2,0,0.1,0.4,0.4,0.3,0.4,0.6,0.4,0.6,0.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}],%22region%22:{%22row%22:%22Human%22,%22start%22:170,%22end%22:290}},{%22type%22:%22ProteinView%22,%22uniprotId%22:%22P04637%22,%22transcriptId%22:%22NM_000546.6%22,%22connectedViewId%22:%22lgv1%22,%22connectedView%22:{%22assembly%22:%22hg38%22,%22loc%22:%22chr17:7,661,779-7,687,538%22,%22tracks%22:[%22hg38-ncbiRefSeqSelect%22]}}]}):
|
|
18
|
+
TP53 in hg38 with ClinVar, a vertebrate p53 alignment carrying ClinVar,
|
|
19
|
+
AlphaMissense and MaveDB per-residue tracks, and the AlphaFold model of P04637.
|
|
20
|
+
Hovering a codon, a column or a residue lights the other two views. See
|
|
21
|
+
[DEVELOPERS.md](DEVELOPERS.md#demo-p53-variant-evidence) for how the link is
|
|
22
|
+
built.
|
|
23
|
+
|
|
24
|
+
An older shared session:
|
|
17
25
|
https://jbrowse.org/code/jb2/main/index.html?config=https://unpkg.com/jbrowse-plugin-msaview/dist/config.json&session=share-BVmmEYAoAv&password=SuQaN
|
|
18
26
|
|
|
19
27
|
## Features
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
+
import { genomeToTranscriptSeqMapping } from 'g2p_mapper';
|
|
3
|
+
import { describe, expect, test } from 'vitest';
|
|
4
|
+
import { transcriptPosToVisibleCol } from '../MsaViewPanel/util';
|
|
5
|
+
import { getProteinSequenceFromFeature } from './components/calculateProteinSequence';
|
|
6
|
+
import { findQueryRow } from './detectQueryRow';
|
|
7
|
+
import { cleanProteinSequence } from './util';
|
|
8
|
+
describe('cleanProteinSequence', () => {
|
|
9
|
+
test('drops the stop and a trailing partial codon', () => {
|
|
10
|
+
expect(cleanProteinSequence('MAWK*')).toBe('MAWK');
|
|
11
|
+
expect(cleanProteinSequence('MAWK&')).toBe('MAWK');
|
|
12
|
+
});
|
|
13
|
+
test('keeps a residue for every other codon', () => {
|
|
14
|
+
expect(cleanProteinSequence('&MAWK*')).toBe('XMAWK');
|
|
15
|
+
expect(cleanProteinSequence('MA*WK*')).toBe('MAXWK');
|
|
16
|
+
expect(cleanProteinSequence('MA&WK')).toBe('MAXWK');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
// what the genome view hovers is a codon, and what lights is the query row's
|
|
20
|
+
// residue at g2p's number for that codon, so the two have to count alike
|
|
21
|
+
describe('a launched query row read through g2p', () => {
|
|
22
|
+
function rowResidueUnder(genomePos, json) {
|
|
23
|
+
const seq = json.seq;
|
|
24
|
+
const feature = new SimpleFeature(json);
|
|
25
|
+
const row = cleanProteinSequence(getProteinSequenceFromFeature({ seq, feature }));
|
|
26
|
+
const { g2p } = genomeToTranscriptSeqMapping(feature.toJSON());
|
|
27
|
+
const col = transcriptPosToVisibleCol({
|
|
28
|
+
querySeqName: 'QUERY',
|
|
29
|
+
querySeqOffset: 0,
|
|
30
|
+
seqPosToVisibleCol: (_, p) => (p < row.length ? p : row.length),
|
|
31
|
+
visibleColToSeqPos: (_, c) => (c < row.length ? c : undefined),
|
|
32
|
+
}, g2p[genomePos]);
|
|
33
|
+
return row[col];
|
|
34
|
+
}
|
|
35
|
+
function transcript(seq, cds) {
|
|
36
|
+
return {
|
|
37
|
+
uniqueId: 't1',
|
|
38
|
+
refName: 'chr1',
|
|
39
|
+
start: 0,
|
|
40
|
+
end: seq.length,
|
|
41
|
+
strand: 1,
|
|
42
|
+
type: 'mRNA',
|
|
43
|
+
seq,
|
|
44
|
+
subfeatures: [
|
|
45
|
+
{ uniqueId: 'c1', refName: 'chr1', start: 0, end: seq.length, ...cds },
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
test('after a partial first codon', () => {
|
|
50
|
+
// G | ATG GCC TGG AAA TAA: base 4 opens GCC, alanine
|
|
51
|
+
const json = transcript('GATGGCCTGGAAATAA', { type: 'CDS', phase: 1 });
|
|
52
|
+
expect(rowResidueUnder(4, json)).toBe('A');
|
|
53
|
+
expect(rowResidueUnder(10, json)).toBe('K');
|
|
54
|
+
});
|
|
55
|
+
test('after an internal stop', () => {
|
|
56
|
+
// ATG TGA GCC TGG TAA: a selenocysteine GENCODE does not annotate
|
|
57
|
+
const json = transcript('ATGTGAGCCTGGTAA', { type: 'CDS' });
|
|
58
|
+
expect(rowResidueUnder(6, json)).toBe('A');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('query row detection', () => {
|
|
62
|
+
test('matches a row a search launch built from a partial transcript', () => {
|
|
63
|
+
const msa = '>QUERY\nXMAWK\n>other\nMAWR-';
|
|
64
|
+
expect(findQueryRow(msa, '&MAWK*').match).toMatchObject({
|
|
65
|
+
name: 'QUERY',
|
|
66
|
+
quality: 'exact',
|
|
67
|
+
offset: 0,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
test('still places a row that starts at the first whole codon', () => {
|
|
71
|
+
const msa = '>Query_1\nMAWK\n>other\nMAWR';
|
|
72
|
+
expect(findQueryRow(msa, '&MAWK*').match).toMatchObject({
|
|
73
|
+
name: 'Query_1',
|
|
74
|
+
offset: 1,
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -9,10 +9,10 @@ import LaunchPanelContent from '../LaunchPanelContent';
|
|
|
9
9
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
10
10
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
11
11
|
import TranscriptSelector from '../TranscriptSelector';
|
|
12
|
+
import { builtAlignmentLook, launchConnectedView, useLaunchSubmit, } from '../launchConnectedView';
|
|
12
13
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
13
14
|
import CachedBlastResults from './CachedBlastResults';
|
|
14
15
|
import MsaAlgorithmSelect from './MsaAlgorithmSelect';
|
|
15
|
-
import { blastLaunchView } from './blastLaunchView';
|
|
16
16
|
import { databaseLabel, databaseOptionsFor, defaultMaxHits, defaultSearchFor, searchProgramLabels, searchPrograms, } from './consts';
|
|
17
17
|
import { useStoredMsaAlgorithm, useStoredSearchChoice, } from './searchChoiceStorage';
|
|
18
18
|
import { useCachedBlastResults } from './useCachedBlastResults';
|
|
@@ -35,7 +35,7 @@ const useStyles = makeStyles()({
|
|
|
35
35
|
const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
|
|
36
36
|
const { classes } = useStyles();
|
|
37
37
|
const view = getLinearGenomeView(model);
|
|
38
|
-
const
|
|
38
|
+
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
39
39
|
// one piece of state, not two: a program and a database that program does not
|
|
40
40
|
// have is a 400 from EBI minutes after Submit, and holding them apart is what
|
|
41
41
|
// would let them drift into that
|
|
@@ -53,7 +53,7 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
53
53
|
preferredTranscriptId,
|
|
54
54
|
});
|
|
55
55
|
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
56
|
-
const e = transcriptSelection.error ??
|
|
56
|
+
const e = transcriptSelection.error ?? launchError ?? cachedResultsError;
|
|
57
57
|
return (React.createElement(React.Fragment, null,
|
|
58
58
|
React.createElement(LaunchPanelContent, { error: e },
|
|
59
59
|
children,
|
|
@@ -85,37 +85,34 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
85
85
|
React.createElement(Typography, null, "Previous BLAST Results")),
|
|
86
86
|
React.createElement(AccordionDetails, null,
|
|
87
87
|
React.createElement(CachedBlastResults, { model: model, handleClose: handleClose, feature: feature })))) : null),
|
|
88
|
-
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !hitCountValid, onSubmit:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !hitCountValid, onSubmit: placement => {
|
|
89
|
+
if (selectedTranscript) {
|
|
90
|
+
const blastParams = search.program === 'phmmer'
|
|
91
|
+
? {
|
|
92
|
+
searchProgram: 'phmmer',
|
|
93
|
+
blastDatabase: search.database,
|
|
94
|
+
maxHits: hitCount,
|
|
95
|
+
selectedTranscript: selectedTranscript.toJSON(),
|
|
96
|
+
proteinSequence,
|
|
97
|
+
}
|
|
98
|
+
: {
|
|
99
|
+
searchProgram: 'blastp',
|
|
100
|
+
blastDatabase: search.database,
|
|
101
|
+
msaAlgorithm: selectedMsaAlgorithm,
|
|
102
|
+
maxHits: hitCount,
|
|
103
|
+
selectedTranscript: selectedTranscript.toJSON(),
|
|
104
|
+
proteinSequence,
|
|
105
|
+
};
|
|
106
|
+
submit(() => {
|
|
107
|
+
launchConnectedView({
|
|
94
108
|
view,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
maxHits: hitCount,
|
|
101
|
-
selectedTranscript: selectedTranscript.toJSON(),
|
|
102
|
-
proteinSequence,
|
|
103
|
-
}
|
|
104
|
-
: {
|
|
105
|
-
searchProgram: 'blastp',
|
|
106
|
-
blastDatabase: search.database,
|
|
107
|
-
msaAlgorithm: selectedMsaAlgorithm,
|
|
108
|
-
maxHits: hitCount,
|
|
109
|
-
selectedTranscript: selectedTranscript.toJSON(),
|
|
110
|
-
proteinSequence,
|
|
111
|
-
},
|
|
109
|
+
feature: selectedTranscript,
|
|
110
|
+
placement,
|
|
111
|
+
displayName: getBlastViewTitle(feature, selectedTranscript),
|
|
112
|
+
...builtAlignmentLook,
|
|
113
|
+
blastParams,
|
|
112
114
|
});
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
catch (e) {
|
|
117
|
-
console.error(e);
|
|
118
|
-
setLaunchViewError(e);
|
|
115
|
+
});
|
|
119
116
|
}
|
|
120
117
|
}, onCancel: handleClose })));
|
|
121
118
|
});
|
|
@@ -8,10 +8,10 @@ import TextField2 from '../../../components/TextField2';
|
|
|
8
8
|
import { useQueryRowName } from '../../useQueryRowName';
|
|
9
9
|
import { cleanProteinSequence, getGeneDisplayName, getLinearGenomeView, } from '../../util';
|
|
10
10
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
11
|
-
import { launchView } from '../ManualMSALoader/launchView';
|
|
12
11
|
import QueryRowSelector from '../QueryRowSelector';
|
|
13
12
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
14
13
|
import TranscriptSelector from '../TranscriptSelector';
|
|
14
|
+
import { launchConnectedView, useLaunchSubmit } from '../launchConnectedView';
|
|
15
15
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
16
16
|
import { BASE_BLAST_URL } from './consts';
|
|
17
17
|
const useStyles = makeStyles()({
|
|
@@ -46,7 +46,7 @@ const useStyles = makeStyles()({
|
|
|
46
46
|
const BlastManualPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
|
|
47
47
|
const { classes } = useStyles();
|
|
48
48
|
const view = getLinearGenomeView(model);
|
|
49
|
-
const
|
|
49
|
+
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
50
50
|
const [msaText, setMsaText] = useState('');
|
|
51
51
|
const [treeText, setTreeText] = useState('');
|
|
52
52
|
const transcriptSelection = useTranscriptSelection({
|
|
@@ -62,7 +62,7 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
|
|
|
62
62
|
const link = `${BASE_BLAST_URL}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${s2}`;
|
|
63
63
|
const link2 = `${BASE_BLAST_URL}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${shorten2(s2, 10)}`;
|
|
64
64
|
return (React.createElement(React.Fragment, null,
|
|
65
|
-
React.createElement(LaunchPanelContent, { error:
|
|
65
|
+
React.createElement(LaunchPanelContent, { error: launchError ?? error },
|
|
66
66
|
children,
|
|
67
67
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
68
68
|
React.createElement("div", { className: classes.step },
|
|
@@ -83,24 +83,19 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
|
|
|
83
83
|
setTreeText(event.target.value);
|
|
84
84
|
} }),
|
|
85
85
|
React.createElement(QueryRowSelector, { ...queryRow })))),
|
|
86
|
-
React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript || !msaText.trim(), onSubmit:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
launchView({
|
|
91
|
-
newViewTitle: getGeneDisplayName(selectedTranscript),
|
|
86
|
+
React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript || !msaText.trim(), onSubmit: placement => {
|
|
87
|
+
if (selectedTranscript) {
|
|
88
|
+
submit(() => {
|
|
89
|
+
launchConnectedView({
|
|
92
90
|
view,
|
|
93
91
|
feature: selectedTranscript,
|
|
92
|
+
placement,
|
|
93
|
+
displayName: getGeneDisplayName(selectedTranscript),
|
|
94
94
|
querySeqName: queryRow.querySeqName,
|
|
95
95
|
querySeqOffset: queryRow.querySeqOffset,
|
|
96
96
|
data: { msa: msaText, tree: treeText },
|
|
97
97
|
});
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch (e) {
|
|
102
|
-
console.error(e);
|
|
103
|
-
setLaunchViewError(e);
|
|
98
|
+
});
|
|
104
99
|
}
|
|
105
100
|
}, onCancel: handleClose })));
|
|
106
101
|
});
|
|
@@ -5,7 +5,8 @@ import { Button, IconButton, List, ListItem, ListItemButton, ListItemText, Typog
|
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
6
|
import { makeStyles } from 'tss-react/mui';
|
|
7
7
|
import { featureMatchesId, getGeneIdentifiers, getLinearGenomeView, getSortedTranscriptFeatures, } from '../../util';
|
|
8
|
-
import {
|
|
8
|
+
import { builtAlignmentLook, launchConnectedView } from '../launchConnectedView';
|
|
9
|
+
import { useLaunchPlacement } from '../launchPlacement';
|
|
9
10
|
import { useCachedBlastResults } from './useCachedBlastResults';
|
|
10
11
|
const useStyles = makeStyles()({
|
|
11
12
|
header: {
|
|
@@ -53,22 +54,22 @@ const CachedBlastResults = observer(function ({ model, handleClose, feature, })
|
|
|
53
54
|
const { classes } = useStyles();
|
|
54
55
|
const view = getLinearGenomeView(model);
|
|
55
56
|
const [operationError, setOperationError] = useState();
|
|
57
|
+
const [sideBySide] = useLaunchPlacement();
|
|
56
58
|
const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
57
59
|
const { results, error, isLoading, handleDelete, handleClearAll } = useCachedBlastResults(geneIds);
|
|
58
60
|
const handleUseCached = (cached) => {
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const { transcriptId } = cached;
|
|
64
|
-
const transcript = transcriptId
|
|
65
|
-
? getSortedTranscriptFeatures(feature).find(t => featureMatchesId(t, transcriptId))
|
|
66
|
-
: undefined;
|
|
67
|
-
blastLaunchViewFromCache({
|
|
61
|
+
// the cached query row is the plugin's default `QUERY`, translated from
|
|
62
|
+
// the transcript stored as transcriptId, so that transcript relinks it
|
|
63
|
+
const { transcriptId, msa, tree, treeMetadata } = cached;
|
|
64
|
+
launchConnectedView({
|
|
68
65
|
view,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
feature: transcriptId
|
|
67
|
+
? getSortedTranscriptFeatures(feature).find(t => featureMatchesId(t, transcriptId))
|
|
68
|
+
: undefined,
|
|
69
|
+
placement: sideBySide ? 'splitRight' : 'stack',
|
|
70
|
+
displayName: `BLAST - ${getResultDisplayName(cached)}`,
|
|
71
|
+
...builtAlignmentLook,
|
|
72
|
+
data: { msa, tree, treeMetadata },
|
|
72
73
|
});
|
|
73
74
|
handleClose();
|
|
74
75
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { FileSelector } from '@jbrowse/core/ui';
|
|
3
|
+
import { openLocation } from '@jbrowse/core/util/io';
|
|
3
4
|
import { FormControl, FormControlLabel, Radio, RadioGroup } from '@mui/material';
|
|
4
5
|
import { observer } from 'mobx-react';
|
|
5
6
|
import { makeStyles } from 'tss-react/mui';
|
|
6
7
|
import TextField2 from '../../../components/TextField2';
|
|
8
|
+
import { useDebounced, useFetch } from '../../../utils/useFetch';
|
|
7
9
|
import { useQueryRowName } from '../../useQueryRowName';
|
|
8
10
|
import { getGeneDisplayName, getLinearGenomeView } from '../../util';
|
|
9
11
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
@@ -11,8 +13,18 @@ import QueryRowSelector from '../QueryRowSelector';
|
|
|
11
13
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
12
14
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
13
15
|
import TranscriptSelector from '../TranscriptSelector';
|
|
16
|
+
import { launchConnectedView, useLaunchSubmit } from '../launchConnectedView';
|
|
14
17
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The chosen file's text, so its query row is found by sequence the way a
|
|
20
|
+
* pasted alignment's is; without it a file launch named no row and never
|
|
21
|
+
* linked to the genome. Debounced because a URL arrives a keystroke at a time.
|
|
22
|
+
*/
|
|
23
|
+
function useMsaFileText(location) {
|
|
24
|
+
const debounced = useDebounced(location, 500);
|
|
25
|
+
const { data } = useFetch(debounced ? [JSON.stringify(debounced), 'msa-file-text'] : null, () => openLocation(debounced).readFile('utf8'));
|
|
26
|
+
return data ?? '';
|
|
27
|
+
}
|
|
16
28
|
const useStyles = makeStyles()({
|
|
17
29
|
textAreaFont: {
|
|
18
30
|
fontFamily: 'Courier New',
|
|
@@ -30,7 +42,7 @@ const useStyles = makeStyles()({
|
|
|
30
42
|
const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handleClose, preferredTranscriptId, }) {
|
|
31
43
|
const view = getLinearGenomeView(model);
|
|
32
44
|
const { classes } = useStyles();
|
|
33
|
-
const
|
|
45
|
+
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
34
46
|
const [inputMethod, setInputMethod] = useState('file');
|
|
35
47
|
const [msaText, setMsaText] = useState('');
|
|
36
48
|
const [treeText, setTreeText] = useState('');
|
|
@@ -42,8 +54,9 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
42
54
|
preferredTranscriptId,
|
|
43
55
|
});
|
|
44
56
|
const { selectedTranscript, proteinSequence, error, sequenceStatus } = transcriptSelection;
|
|
45
|
-
const
|
|
46
|
-
const
|
|
57
|
+
const msaFileText = useMsaFileText(inputMethod === 'file' ? msaFileLocation : undefined);
|
|
58
|
+
const queryRow = useQueryRowName(inputMethod === 'file' ? msaFileText : msaText, proteinSequence);
|
|
59
|
+
const e = launchError ?? error;
|
|
47
60
|
return (React.createElement(React.Fragment, null,
|
|
48
61
|
React.createElement(LaunchPanelContent, { error: e },
|
|
49
62
|
React.createElement(FormControl, { component: "fieldset" },
|
|
@@ -65,14 +78,14 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
65
78
|
React.createElement(QueryRowSelector, { ...queryRow })),
|
|
66
79
|
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !selectedTranscript ||
|
|
67
80
|
(inputMethod === 'file' && !msaFileLocation) ||
|
|
68
|
-
(inputMethod === 'text' && !msaText.trim()), onSubmit:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
launchView({
|
|
73
|
-
newViewTitle: getGeneDisplayName(selectedTranscript),
|
|
81
|
+
(inputMethod === 'text' && !msaText.trim()), onSubmit: placement => {
|
|
82
|
+
if (selectedTranscript) {
|
|
83
|
+
submit(() => {
|
|
84
|
+
launchConnectedView({
|
|
74
85
|
view,
|
|
75
86
|
feature: selectedTranscript,
|
|
87
|
+
placement,
|
|
88
|
+
displayName: getGeneDisplayName(selectedTranscript),
|
|
76
89
|
querySeqName: queryRow.querySeqName,
|
|
77
90
|
querySeqOffset: queryRow.querySeqOffset,
|
|
78
91
|
...(inputMethod === 'file'
|
|
@@ -80,19 +93,9 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
80
93
|
msaFilehandle: msaFileLocation,
|
|
81
94
|
treeFilehandle: treeFileLocation,
|
|
82
95
|
}
|
|
83
|
-
: {
|
|
84
|
-
data: {
|
|
85
|
-
msa: msaText,
|
|
86
|
-
tree: treeText,
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
96
|
+
: { data: { msa: msaText, tree: treeText } }),
|
|
89
97
|
});
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
94
|
-
console.error(err);
|
|
95
|
-
setLaunchViewError(err);
|
|
98
|
+
});
|
|
96
99
|
}
|
|
97
100
|
}, onCancel: handleClose })));
|
|
98
101
|
});
|
|
@@ -4,7 +4,6 @@ import { observer } from 'mobx-react';
|
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
import TextField2 from '../../../components/TextField2';
|
|
6
6
|
import { defaultMaxSpecies } from '../../../utils/ncbiOrthologs';
|
|
7
|
-
import { useLocalStorage } from '../../../utils/useLocalStorage';
|
|
8
7
|
import { getGeneDisplayName, getGeneIdentifiers, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
|
|
9
8
|
import MsaAlgorithmSelect from '../BlastQuery/MsaAlgorithmSelect';
|
|
10
9
|
import { useStoredMsaAlgorithm } from '../BlastQuery/searchChoiceStorage';
|
|
@@ -12,10 +11,10 @@ import LaunchPanelContent from '../LaunchPanelContent';
|
|
|
12
11
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
13
12
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
14
13
|
import TranscriptSelector from '../TranscriptSelector';
|
|
14
|
+
import { builtAlignmentLook, launchConnectedView, useLaunchSubmit, } from '../launchConnectedView';
|
|
15
15
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
16
|
-
import OrthologSourceSelect, {
|
|
16
|
+
import OrthologSourceSelect, { useStoredOrthologSource, } from './OrthologSourceSelect';
|
|
17
17
|
import QuerySpeciesSelect from './QuerySpeciesSelect';
|
|
18
|
-
import { orthologLaunchView } from './orthologLaunchView';
|
|
19
18
|
const useStyles = makeStyles()({
|
|
20
19
|
selectField: {
|
|
21
20
|
width: 180,
|
|
@@ -31,9 +30,9 @@ const rowsHint = {
|
|
|
31
30
|
const OrthologPanel = observer(function ({ handleClose, feature, model, preferredTranscriptId, }) {
|
|
32
31
|
const { classes } = useStyles();
|
|
33
32
|
const view = getLinearGenomeView(model);
|
|
34
|
-
const
|
|
33
|
+
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
35
34
|
const [taxId, setTaxId] = useState(9606);
|
|
36
|
-
const [source, setSource] =
|
|
35
|
+
const [source, setSource] = useStoredOrthologSource();
|
|
37
36
|
const [msaAlgorithm, setMsaAlgorithm] = useStoredMsaAlgorithm();
|
|
38
37
|
const [maxSpecies, setMaxSpecies] = useState(String(defaultMaxSpecies));
|
|
39
38
|
const geneCandidates = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
@@ -43,7 +42,7 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, preferre
|
|
|
43
42
|
preferredTranscriptId,
|
|
44
43
|
});
|
|
45
44
|
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
46
|
-
const e = transcriptSelection.error ??
|
|
45
|
+
const e = transcriptSelection.error ?? launchError;
|
|
47
46
|
const rowCount = Number(maxSpecies);
|
|
48
47
|
const rowCountValid = Number.isInteger(rowCount) && rowCount >= 2;
|
|
49
48
|
return (React.createElement(React.Fragment, null,
|
|
@@ -59,14 +58,15 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, preferre
|
|
|
59
58
|
? rowsHint[source](rowCount)
|
|
60
59
|
: 'a whole number, 2 or more' })),
|
|
61
60
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection })),
|
|
62
|
-
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !rowCountValid, onSubmit:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
orthologLaunchView({
|
|
67
|
-
feature: selectedTranscript,
|
|
61
|
+
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !rowCountValid, onSubmit: placement => {
|
|
62
|
+
if (selectedTranscript) {
|
|
63
|
+
submit(() => {
|
|
64
|
+
launchConnectedView({
|
|
68
65
|
view,
|
|
69
|
-
|
|
66
|
+
feature: selectedTranscript,
|
|
67
|
+
placement,
|
|
68
|
+
displayName: `Orthologs - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
|
|
69
|
+
...builtAlignmentLook,
|
|
70
70
|
orthologParams: {
|
|
71
71
|
taxId,
|
|
72
72
|
source,
|
|
@@ -77,12 +77,7 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, preferre
|
|
|
77
77
|
proteinSequence,
|
|
78
78
|
},
|
|
79
79
|
});
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch (e) {
|
|
84
|
-
console.error(e);
|
|
85
|
-
setLaunchViewError(e);
|
|
80
|
+
});
|
|
86
81
|
}
|
|
87
82
|
}, onCancel: handleClose })));
|
|
88
83
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { OrthologSource } from '../../../MsaViewPanel/model';
|
|
3
|
-
export declare const ORTHOLOG_SOURCE_STORAGE_KEY = "msaview-ortholog-source";
|
|
4
3
|
export declare const orthologSourceLabels: Record<OrthologSource, string>;
|
|
4
|
+
export declare function validOrthologSource(stored: unknown): OrthologSource;
|
|
5
|
+
export declare function useStoredOrthologSource(): readonly ["ncbi" | "panther" | "uniref", (source: OrthologSource) => void];
|
|
5
6
|
export default function OrthologSourceSelect({ value, onChange, className, }: {
|
|
6
7
|
value: OrthologSource;
|
|
7
8
|
onChange: (val: OrthologSource) => void;
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { MenuItem } from '@mui/material';
|
|
3
3
|
import TextField2 from '../../../components/TextField2';
|
|
4
|
-
|
|
4
|
+
import { useLocalStorage } from '../../../utils/useLocalStorage';
|
|
5
|
+
const ORTHOLOG_SOURCE_STORAGE_KEY = 'msaview-ortholog-source';
|
|
5
6
|
export const orthologSourceLabels = {
|
|
6
7
|
ncbi: 'NCBI orthologs',
|
|
7
8
|
panther: 'PANTHER',
|
|
8
9
|
uniref: 'UniRef cluster',
|
|
9
10
|
};
|
|
11
|
+
const orthologSources = Object.keys(orthologSourceLabels);
|
|
12
|
+
// another plugin version on the same origin may have stored a source this one
|
|
13
|
+
// lacks, and the tab indexes its hints by it
|
|
14
|
+
export function validOrthologSource(stored) {
|
|
15
|
+
return orthologSources.find(s => s === stored) ?? 'ncbi';
|
|
16
|
+
}
|
|
17
|
+
export function useStoredOrthologSource() {
|
|
18
|
+
const [stored, setStored] = useLocalStorage(ORTHOLOG_SOURCE_STORAGE_KEY, 'ncbi');
|
|
19
|
+
return [
|
|
20
|
+
validOrthologSource(stored),
|
|
21
|
+
(source) => {
|
|
22
|
+
setStored(source);
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
}
|
|
10
26
|
// Which species a source can answer for, in the words a reader picking one
|
|
11
27
|
// needs: NCBI's ortholog sets stop at vertebrates and insects, PANTHER's run
|
|
12
28
|
// from human to yeast and Arabidopsis, and a UniRef cluster is every UniProtKB
|
|
@@ -19,5 +35,5 @@ const hints = {
|
|
|
19
35
|
export default function OrthologSourceSelect({ value, onChange, className, }) {
|
|
20
36
|
return (React.createElement(TextField2, { variant: "outlined", label: "Source", className: className, select: true, value: value, helperText: hints[value], onChange: event => {
|
|
21
37
|
onChange(event.target.value);
|
|
22
|
-
} },
|
|
38
|
+
} }, orthologSources.map(val => (React.createElement(MenuItem, { value: val, key: val }, orthologSourceLabels[val])))));
|
|
23
39
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { validOrthologSource } from './OrthologSourceSelect';
|
|
3
|
+
test('a stored source this version knows comes back as it went in', () => {
|
|
4
|
+
expect(validOrthologSource('panther')).toBe('panther');
|
|
5
|
+
expect(validOrthologSource('uniref')).toBe('uniref');
|
|
6
|
+
});
|
|
7
|
+
// the Orthologs tab is the dialog's default, and it looked its helper text up
|
|
8
|
+
// by whatever was stored, so an unknown value threw on open
|
|
9
|
+
test('anything else falls back to NCBI', () => {
|
|
10
|
+
expect(validOrthologSource('ensembl')).toBe('ncbi');
|
|
11
|
+
expect(validOrthologSource(undefined)).toBe('ncbi');
|
|
12
|
+
expect(validOrthologSource({ source: 'panther' })).toBe('ncbi');
|
|
13
|
+
});
|
|
@@ -13,9 +13,9 @@ import QueryRowSelector from '../QueryRowSelector';
|
|
|
13
13
|
import SequenceStatusMessage from '../SequenceStatus';
|
|
14
14
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
15
15
|
import TranscriptSelector from '../TranscriptSelector';
|
|
16
|
+
import { launchConnectedView, useLaunchSubmit } from '../launchConnectedView';
|
|
16
17
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
17
18
|
import { fetchMSA, fetchMSAList } from './fetchMSAData';
|
|
18
|
-
import { preCalculatedLaunchView } from './preCalculatedLaunchView';
|
|
19
19
|
import { readMsaDatasets } from './types';
|
|
20
20
|
const useStyles = makeStyles()({
|
|
21
21
|
selectedContainer: {
|
|
@@ -28,7 +28,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
28
28
|
const { classes } = useStyles();
|
|
29
29
|
const { pluginManager } = getEnv(model);
|
|
30
30
|
const { assemblyNames } = view;
|
|
31
|
-
const
|
|
31
|
+
const { launchError, submit } = useLaunchSubmit(handleClose);
|
|
32
32
|
const datasets = readMsaDatasets(session.jbrowse);
|
|
33
33
|
const [selectedDatasetId, setSelectedDatasetId] = useState(datasets?.[0]?.datasetId);
|
|
34
34
|
const selectedDataset = datasets?.find(d => d.datasetId === selectedDatasetId);
|
|
@@ -63,7 +63,7 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
63
63
|
const e = msaListFetchError ??
|
|
64
64
|
msaDataFetchError ??
|
|
65
65
|
transcriptSelection.error ??
|
|
66
|
-
|
|
66
|
+
launchError;
|
|
67
67
|
return (React.createElement(React.Fragment, null,
|
|
68
68
|
React.createElement(LaunchPanelContent, { error: e },
|
|
69
69
|
React.createElement(TextField2, { select: true, label: "Select MSA dataset", value: selectedDatasetId, onChange: event => {
|
|
@@ -80,22 +80,27 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
|
|
|
80
80
|
// launching without a query row opens a view that renders and then
|
|
81
81
|
// never navigates, which reads as a broken feature rather than a
|
|
82
82
|
// dataset that does not cover this gene
|
|
83
|
-
submitDisabled: !selectedTranscript || !msaData?.length || !querySeqName, onSubmit:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
newViewTitle: getGeneDisplayName(selectedTranscript),
|
|
83
|
+
submitDisabled: !selectedTranscript || !msaData?.length || !querySeqName, onSubmit: placement => {
|
|
84
|
+
if (selectedTranscript && msaText) {
|
|
85
|
+
submit(() => {
|
|
86
|
+
launchConnectedView({
|
|
88
87
|
view,
|
|
88
|
+
feature: selectedTranscript,
|
|
89
|
+
placement,
|
|
90
|
+
displayName: getGeneDisplayName(selectedTranscript),
|
|
91
|
+
treeAreaWidth: 200,
|
|
92
|
+
treeWidth: 100,
|
|
93
|
+
drawNodeBubbles: false,
|
|
94
|
+
labelsAlignRight: true,
|
|
95
|
+
showBranchLen: false,
|
|
96
|
+
colWidth: 10,
|
|
97
|
+
rowHeight: 12,
|
|
98
|
+
colorSchemeName: 'percent_identity_dynamic',
|
|
89
99
|
querySeqName,
|
|
90
100
|
querySeqOffset: queryRow.querySeqOffset,
|
|
91
|
-
feature: selectedTranscript,
|
|
92
101
|
data: { msa: msaText },
|
|
93
102
|
});
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
setViewError(e);
|
|
103
|
+
});
|
|
99
104
|
}
|
|
100
105
|
}, onCancel: handleClose })));
|
|
101
106
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { MsaViewPlacement } from '../../utils/workspaces';
|
|
2
3
|
import type { AbstractTrackModel } from '@jbrowse/core/util';
|
|
3
4
|
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel, cancelLabel, model, }: {
|
|
4
|
-
|
|
5
|
+
/** handed the placement the box states, so no launch re-reads storage */
|
|
6
|
+
onSubmit: (placement: MsaViewPlacement) => void;
|
|
5
7
|
onCancel: () => void;
|
|
6
8
|
submitDisabled?: boolean;
|
|
7
9
|
/** why Submit is grey, shown beside it */
|