jbrowse-plugin-msaview 3.4.1 → 3.6.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 +7 -0
- package/dist/AddHighlightModel/index.js +3 -3
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +8 -2
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
- package/dist/BgzipFastaMsaAdapter/configSchema.js +1 -1
- package/dist/LaunchMsaView/codingFeature.d.ts +14 -0
- package/dist/LaunchMsaView/codingFeature.js +38 -0
- package/dist/LaunchMsaView/codingFeature.test.js +75 -0
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +29 -26
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +7 -2
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.d.ts +7 -3
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +8 -7
- package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.js +2 -2
- package/dist/LaunchMsaView/components/BlastQuery/MsaAlgorithmSelect.js +5 -3
- package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +26 -4
- package/dist/LaunchMsaView/components/BlastQuery/consts.js +68 -2
- package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.d.ts +1 -1
- package/dist/LaunchMsaView/components/HelpButton.d.ts +2 -0
- package/dist/LaunchMsaView/components/HelpButton.js +17 -0
- package/dist/LaunchMsaView/components/HelpDialog.d.ts +4 -0
- package/dist/LaunchMsaView/components/HelpDialog.js +18 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +3 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +3 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +10 -4
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +3 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +2 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +21 -7
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +4 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +33 -15
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +3 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +2 -1
- package/dist/LaunchMsaView/components/QueryRowSelector.js +21 -5
- package/dist/LaunchMsaView/components/SequenceStatus.d.ts +14 -0
- package/dist/LaunchMsaView/components/SequenceStatus.js +15 -0
- package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +3 -1
- package/dist/LaunchMsaView/components/SubmitCancelActions.js +25 -13
- package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +33 -3
- package/dist/LaunchMsaView/components/TabPanel.js +15 -3
- package/dist/LaunchMsaView/components/TabPanel.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/TabPanel.test.js +36 -0
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -1
- package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +4 -1
- package/dist/LaunchMsaView/components/calculateProteinSequence.js +20 -10
- package/dist/LaunchMsaView/components/fetchSeq.d.ts +4 -1
- package/dist/LaunchMsaView/components/fetchSeq.js +14 -4
- package/dist/LaunchMsaView/components/launchPlacement.d.ts +8 -0
- package/dist/LaunchMsaView/components/launchPlacement.js +25 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +2 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.js +14 -11
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +10 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +21 -3
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.js +57 -0
- package/dist/LaunchMsaView/detectQueryRow.d.ts +11 -0
- package/dist/LaunchMsaView/detectQueryRow.js +11 -4
- package/dist/LaunchMsaView/detectQueryRow.test.js +30 -0
- package/dist/LaunchMsaView/index.js +28 -9
- package/dist/LaunchMsaView/launchTarget.d.ts +36 -8
- package/dist/LaunchMsaView/launchTarget.js +23 -13
- package/dist/LaunchMsaView/launchTarget.test.js +80 -15
- package/dist/LaunchMsaView/useQueryRowName.d.ts +13 -0
- package/dist/LaunchMsaView/useQueryRowName.js +16 -1
- package/dist/LaunchMsaView/useQueryRowName.test.d.ts +1 -0
- package/dist/LaunchMsaView/useQueryRowName.test.js +40 -0
- package/dist/LaunchMsaView/util.js +5 -2
- package/dist/LaunchMsaViewExtensionPoint/index.js +8 -6
- package/dist/LaunchMsaViewExtensionPoint/index.test.js +21 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +9 -6
- package/dist/MsaViewPanel/afterCreateAutoruns.js +72 -28
- package/dist/MsaViewPanel/components/LaunchProgress.d.ts +4 -3
- package/dist/MsaViewPanel/components/LaunchProgress.js +22 -10
- package/dist/MsaViewPanel/components/MsaViewPanel.js +10 -2
- package/dist/MsaViewPanel/components/MsaViewPanel.test.js +47 -2
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +8 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +78 -93
- package/dist/MsaViewPanel/doLaunchBlast.test.d.ts +1 -0
- package/dist/MsaViewPanel/doLaunchBlast.test.js +158 -0
- package/dist/MsaViewPanel/doLaunchOrthologs.js +40 -5
- package/dist/MsaViewPanel/doLaunchOrthologs.test.js +10 -0
- package/dist/MsaViewPanel/genomeToMSA.js +25 -7
- package/dist/MsaViewPanel/genomeToMSA.test.js +214 -19
- package/dist/MsaViewPanel/loadProteinDomains.d.ts +8 -2
- package/dist/MsaViewPanel/loadProteinDomains.js +18 -9
- package/dist/MsaViewPanel/loadProteinDomains.test.d.ts +1 -0
- package/dist/MsaViewPanel/loadProteinDomains.test.js +41 -0
- package/dist/MsaViewPanel/model.d.ts +265 -103
- package/dist/MsaViewPanel/model.js +174 -48
- package/dist/MsaViewPanel/model.test.d.ts +1 -0
- package/dist/MsaViewPanel/model.test.js +166 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +12 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +8 -8
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +133 -32
- package/dist/MsaViewPanel/msaDataStore.d.ts +2 -0
- package/dist/MsaViewPanel/msaDataStore.js +10 -0
- package/dist/MsaViewPanel/observeProteinHighlights.test.js +20 -3
- package/dist/MsaViewPanel/processInit.test.d.ts +1 -0
- package/dist/MsaViewPanel/processInit.test.js +72 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +9 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.js +128 -0
- package/dist/MsaViewPanel/runLaunch.d.ts +1 -0
- package/dist/MsaViewPanel/runLaunch.js +35 -0
- package/dist/MsaViewPanel/runLaunch.test.js +46 -0
- package/dist/MsaViewPanel/storedData.test.js +20 -0
- package/dist/MsaViewPanel/structureConnection.d.ts +0 -4
- package/dist/MsaViewPanel/structureConnection.js +0 -19
- package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +12 -5
- package/dist/MsaViewPanel/util.d.ts +39 -0
- package/dist/MsaViewPanel/util.js +44 -0
- package/dist/index.d.ts +11 -11
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +56 -68
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/browserAlign.d.ts +42 -0
- package/dist/utils/browserAlign.js +297 -0
- package/dist/utils/browserAlign.test.d.ts +1 -0
- package/dist/utils/browserAlign.test.js +85 -0
- package/dist/utils/ebiBlast.d.ts +10 -1
- package/dist/utils/ebiBlast.js +27 -1
- package/dist/utils/homologSearch.d.ts +31 -0
- package/dist/utils/homologSearch.js +6 -0
- package/dist/utils/msa.d.ts +3 -11
- package/dist/utils/msa.js +14 -29
- package/dist/utils/msaRows.d.ts +11 -8
- package/dist/utils/msaRows.js +14 -12
- package/dist/utils/phmmer.d.ts +11 -1
- package/dist/utils/phmmer.js +49 -12
- package/dist/utils/unirefHomologs.d.ts +94 -0
- package/dist/utils/unirefHomologs.js +193 -0
- package/dist/utils/unirefHomologs.test.d.ts +1 -0
- package/dist/utils/unirefHomologs.test.js +118 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +23 -22
- package/src/AddHighlightModel/index.tsx +1 -1
- package/src/BgzipFastaMsaAdapter/configSchema.ts +1 -1
- package/src/LaunchMsaView/codingFeature.test.ts +96 -0
- package/src/LaunchMsaView/codingFeature.ts +49 -0
- package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +52 -26
- package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +9 -1
- package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +24 -11
- package/src/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.tsx +4 -4
- package/src/LaunchMsaView/components/BlastQuery/MsaAlgorithmSelect.tsx +7 -2
- package/src/LaunchMsaView/components/BlastQuery/consts.ts +77 -2
- package/src/LaunchMsaView/components/HelpButton.tsx +33 -0
- package/src/LaunchMsaView/components/HelpDialog.tsx +78 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +54 -34
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +13 -2
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +4 -0
- package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +28 -5
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +4 -1
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +63 -13
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +4 -0
- package/src/LaunchMsaView/components/QueryRowSelector.tsx +35 -7
- package/src/LaunchMsaView/components/SequenceStatus.tsx +29 -0
- package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +62 -3
- package/src/LaunchMsaView/components/SubmitCancelActions.tsx +37 -15
- package/src/LaunchMsaView/components/TabPanel.test.tsx +51 -0
- package/src/LaunchMsaView/components/TabPanel.tsx +16 -4
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +6 -1
- package/src/LaunchMsaView/components/calculateProteinSequence.ts +29 -10
- package/src/LaunchMsaView/components/fetchSeq.ts +25 -5
- package/src/LaunchMsaView/components/launchPlacement.tsx +41 -0
- package/src/LaunchMsaView/components/useFeatureSequence.ts +18 -11
- package/src/LaunchMsaView/components/useTranscriptSelection.test.tsx +66 -0
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +41 -2
- package/src/LaunchMsaView/detectQueryRow.test.ts +34 -0
- package/src/LaunchMsaView/detectQueryRow.ts +22 -4
- package/src/LaunchMsaView/index.ts +32 -13
- package/src/LaunchMsaView/launchTarget.test.ts +88 -15
- package/src/LaunchMsaView/launchTarget.ts +49 -14
- package/src/LaunchMsaView/useQueryRowName.test.ts +54 -0
- package/src/LaunchMsaView/useQueryRowName.ts +22 -1
- package/src/LaunchMsaView/util.ts +8 -4
- package/src/LaunchMsaViewExtensionPoint/index.test.ts +23 -0
- package/src/LaunchMsaViewExtensionPoint/index.ts +32 -6
- package/src/MsaViewPanel/afterCreateAutoruns.ts +85 -27
- package/src/MsaViewPanel/components/LaunchProgress.tsx +38 -7
- package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +50 -2
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +10 -2
- package/src/MsaViewPanel/doLaunchBlast.test.ts +207 -0
- package/src/MsaViewPanel/doLaunchBlast.ts +92 -142
- package/src/MsaViewPanel/doLaunchOrthologs.test.ts +13 -0
- package/src/MsaViewPanel/doLaunchOrthologs.ts +55 -5
- package/src/MsaViewPanel/genomeToMSA.test.ts +251 -31
- package/src/MsaViewPanel/genomeToMSA.ts +35 -7
- package/src/MsaViewPanel/loadProteinDomains.test.ts +52 -0
- package/src/MsaViewPanel/loadProteinDomains.ts +24 -11
- package/src/MsaViewPanel/model.test.ts +196 -0
- package/src/MsaViewPanel/model.ts +204 -55
- package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +152 -32
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +21 -11
- package/src/MsaViewPanel/msaDataStore.ts +10 -0
- package/src/MsaViewPanel/observeProteinHighlights.test.ts +27 -3
- package/src/MsaViewPanel/processInit.test.ts +84 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.ts +160 -0
- package/src/MsaViewPanel/runLaunch.test.ts +54 -0
- package/src/MsaViewPanel/runLaunch.ts +39 -0
- package/src/MsaViewPanel/storedData.test.ts +26 -0
- package/src/MsaViewPanel/structureConnection.ts +0 -26
- package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +14 -5
- package/src/MsaViewPanel/util.ts +75 -0
- package/src/utils/browserAlign.test.ts +102 -0
- package/src/utils/browserAlign.ts +352 -0
- package/src/utils/ebiBlast.ts +34 -0
- package/src/utils/homologSearch.ts +51 -0
- package/src/utils/msa.ts +19 -41
- package/src/utils/msaRows.ts +20 -16
- package/src/utils/phmmer.ts +57 -11
- package/src/utils/unirefHomologs.test.ts +149 -0
- package/src/utils/unirefHomologs.ts +323 -0
- package/src/version.ts +1 -1
- package/dist/LaunchMsaView/components/geneticCodes.d.ts +0 -15
- package/dist/LaunchMsaView/components/geneticCodes.js +0 -227
- package/dist/MsaViewPanel/structureConnection.test.js +0 -53
- package/src/LaunchMsaView/components/geneticCodes.ts +0 -298
- package/src/MsaViewPanel/structureConnection.test.ts +0 -62
- /package/dist/{MsaViewPanel/structureConnection.test.d.ts → LaunchMsaView/codingFeature.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -29,6 +29,13 @@ https://jbrowse.org/code/jb2/main/index.html?config=https://unpkg.com/jbrowse-pl
|
|
|
29
29
|
- Can share sessions with other users which will send relevant settings and
|
|
30
30
|
links to files to automatically open your results
|
|
31
31
|
- The tree or the MSA panel can be loaded separately from each other
|
|
32
|
+
- Builds a cross-species alignment for any gene from the genome view's
|
|
33
|
+
right-click menu: precomputed orthologs (NCBI, PANTHER), the gene's UniRef
|
|
34
|
+
cluster across all of UniProtKB, or a phmmer/blastp search at EBI
|
|
35
|
+
- Aligns and builds trees in the browser, so a UniRef launch needs no job at any
|
|
36
|
+
external service; EBI's aligners remain an option
|
|
37
|
+
- Every launch is also a session-spec URL (`orthologParams`, `searchParams`,
|
|
38
|
+
`connectedTranscript`), see [DEVELOPERS.md](DEVELOPERS.md)
|
|
32
39
|
|
|
33
40
|
## File format supports
|
|
34
41
|
|
|
@@ -3,9 +3,9 @@ import { getSession } from '@jbrowse/core/util';
|
|
|
3
3
|
import { isMsaView } from '../MsaViewPanel/model';
|
|
4
4
|
import HighlightComponents from './HighlightComponents';
|
|
5
5
|
export default function AddHighlightComponentsModelF(pluginManager) {
|
|
6
|
-
pluginManager.addToExtensionPoint(
|
|
7
|
-
// @ts-expect-error
|
|
8
|
-
(rest, { model }) => {
|
|
6
|
+
pluginManager.addToExtensionPoint(
|
|
7
|
+
// @ts-expect-error v4 hosts have no contributeToExtensionPoint
|
|
8
|
+
'LinearGenomeView-TracksContainerComponent', (rest, { model }) => {
|
|
9
9
|
// Quick check: don't add any components if no MSA view exists
|
|
10
10
|
const { views } = getSession(model);
|
|
11
11
|
const hasMsaView = views.some(v => isMsaView(v) && v.connectedViewId === model.id);
|
|
@@ -3,8 +3,14 @@ import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAda
|
|
|
3
3
|
export default class BgzipFastaMsaAdapter extends BaseAdapter {
|
|
4
4
|
configureP: Promise<BaseFeatureDataAdapter> | undefined;
|
|
5
5
|
refNamesP: Promise<string[]> | undefined;
|
|
6
|
-
configurePre(): Promise<BaseFeatureDataAdapter
|
|
7
|
-
|
|
6
|
+
configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
|
|
7
|
+
setSubschema(slotName: string, data: Record<string, unknown>): any;
|
|
8
|
+
setSlot(slotName: string, value: unknown): void;
|
|
9
|
+
} & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
|
|
10
|
+
configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
|
|
11
|
+
setSubschema(slotName: string, data: Record<string, unknown>): any;
|
|
12
|
+
setSlot(slotName: string, value: unknown): void;
|
|
13
|
+
} & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
|
|
8
14
|
getMSARefs(): Promise<string[]>;
|
|
9
15
|
getMsaRegex(): RegExp;
|
|
10
16
|
refNameToMsaId(refName: string): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
2
|
export default function configSchemaF(pluginManager: PluginManager): import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
3
|
-
msaRegex: {
|
|
4
|
-
type: string;
|
|
5
|
-
defaultValue:
|
|
3
|
+
readonly msaRegex: {
|
|
4
|
+
readonly type: "string";
|
|
5
|
+
readonly defaultValue: "_";
|
|
6
6
|
};
|
|
7
7
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<import("@jbrowse/core/configuration").AnyConfigurationSchemaType, undefined>>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Feature } from '@jbrowse/core/util';
|
|
2
|
+
export declare function isGeneLikeType(type: unknown): boolean;
|
|
3
|
+
export declare function isCodingFeature(feature: Feature): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Whether the feature is known not to code for anything.
|
|
6
|
+
*
|
|
7
|
+
* Not simply `!isCodingFeature`: a feature that arrived with no subfeatures at
|
|
8
|
+
* all says nothing either way, and a host is free to hand one over that way.
|
|
9
|
+
* Reading that as "no protein here" takes the menu item off a perfectly
|
|
10
|
+
* ordinary gene, silently, which is worse than opening a dialog that then has
|
|
11
|
+
* nothing to translate.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isKnownNonCoding(feature: Feature): boolean;
|
|
14
|
+
export declare function geneLikeRoot(feature: Feature): Feature;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copied from @jbrowse/core's featureTypes rather than imported: the barrel
|
|
2
|
+
// only exports it from v5, and a bundle resolving it on a v4 host reads
|
|
3
|
+
// undefined.
|
|
4
|
+
const GENE_LIKE_TYPE = /gene(_segment)?$|rna$|transcript/;
|
|
5
|
+
// `unknown` rather than `string | undefined`, because the type comes off a
|
|
6
|
+
// host's hit-test item and nothing here built it.
|
|
7
|
+
export function isGeneLikeType(type) {
|
|
8
|
+
return typeof type === 'string' && GENE_LIKE_TYPE.test(type.toLowerCase());
|
|
9
|
+
}
|
|
10
|
+
function isCDS(feature) {
|
|
11
|
+
return feature.get('type')?.toLowerCase() === 'cds';
|
|
12
|
+
}
|
|
13
|
+
// The feature itself counts: a viral polyprotein hangs its cleavage products
|
|
14
|
+
// off its CDS rather than off further CDSs.
|
|
15
|
+
export function isCodingFeature(feature) {
|
|
16
|
+
return isCDS(feature) || !!feature.get('subfeatures')?.some(isCodingFeature);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether the feature is known not to code for anything.
|
|
20
|
+
*
|
|
21
|
+
* Not simply `!isCodingFeature`: a feature that arrived with no subfeatures at
|
|
22
|
+
* all says nothing either way, and a host is free to hand one over that way.
|
|
23
|
+
* Reading that as "no protein here" takes the menu item off a perfectly
|
|
24
|
+
* ordinary gene, silently, which is worse than opening a dialog that then has
|
|
25
|
+
* nothing to translate.
|
|
26
|
+
*/
|
|
27
|
+
export function isKnownNonCoding(feature) {
|
|
28
|
+
return !!feature.get('subfeatures')?.length && !isCodingFeature(feature);
|
|
29
|
+
}
|
|
30
|
+
// The outermost gene-like ancestor, so a click on an isoform opens the dialog
|
|
31
|
+
// on the gene with every transcript to choose from, as the canvas host does.
|
|
32
|
+
export function geneLikeRoot(feature) {
|
|
33
|
+
let root = feature;
|
|
34
|
+
for (let parent = root.parent?.(); parent && isGeneLikeType(parent.get('type')); parent = parent.parent?.()) {
|
|
35
|
+
root = parent;
|
|
36
|
+
}
|
|
37
|
+
return root;
|
|
38
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { geneLikeRoot, isCodingFeature, isGeneLikeType, isKnownNonCoding, } from './codingFeature';
|
|
4
|
+
function feature(type, subfeatures = []) {
|
|
5
|
+
return new SimpleFeature({
|
|
6
|
+
uniqueId: `${type}-${Math.random()}`,
|
|
7
|
+
refName: 'chr1',
|
|
8
|
+
start: 0,
|
|
9
|
+
end: 100,
|
|
10
|
+
type,
|
|
11
|
+
subfeatures: subfeatures.map(s => s.toJSON()),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
describe('isGeneLikeType', () => {
|
|
15
|
+
it('accepts the SO spellings of gene, transcript and RNA', () => {
|
|
16
|
+
for (const t of [
|
|
17
|
+
'gene',
|
|
18
|
+
'mRNA',
|
|
19
|
+
'transcript',
|
|
20
|
+
'ncRNA_gene',
|
|
21
|
+
'protein_coding_gene',
|
|
22
|
+
'V_gene_segment',
|
|
23
|
+
'primary_transcript',
|
|
24
|
+
'pseudogenic_transcript',
|
|
25
|
+
'lnc_RNA',
|
|
26
|
+
]) {
|
|
27
|
+
expect(isGeneLikeType(t)).toBe(true);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it('rejects regions, matches and CDS', () => {
|
|
31
|
+
for (const t of ['intergenic_region', 'cDNA_match', 'CDS', 'exon']) {
|
|
32
|
+
expect(isGeneLikeType(t)).toBe(false);
|
|
33
|
+
}
|
|
34
|
+
expect(isGeneLikeType(undefined)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('isCodingFeature', () => {
|
|
38
|
+
it('finds a CDS anywhere below the feature', () => {
|
|
39
|
+
const gene = feature('gene', [
|
|
40
|
+
feature('mRNA', [feature('exon'), feature('CDS')]),
|
|
41
|
+
]);
|
|
42
|
+
expect(isCodingFeature(gene)).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('is false for a transcript with exons only', () => {
|
|
45
|
+
const lnc = feature('lnc_RNA', [feature('exon'), feature('exon')]);
|
|
46
|
+
expect(isCodingFeature(lnc)).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it('counts the feature itself', () => {
|
|
49
|
+
expect(isCodingFeature(feature('CDS'))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('isKnownNonCoding', () => {
|
|
53
|
+
it('is true for a transcript whose subfeatures are all exons', () => {
|
|
54
|
+
expect(isKnownNonCoding(feature('lnc_RNA', [feature('exon'), feature('exon')]))).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
// a host is free to hand over a bare record, and reading that as "no protein
|
|
57
|
+
// here" takes the menu item off an ordinary gene without a word
|
|
58
|
+
it('is false for a feature that arrived with no subfeatures at all', () => {
|
|
59
|
+
expect(isKnownNonCoding(feature('gene'))).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('is false when a CDS is somewhere below', () => {
|
|
62
|
+
expect(isKnownNonCoding(feature('gene', [feature('mRNA', [feature('CDS')])]))).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('geneLikeRoot', () => {
|
|
66
|
+
it('climbs from an isoform to its gene', () => {
|
|
67
|
+
const gene = feature('gene', [feature('mRNA', [feature('CDS')])]);
|
|
68
|
+
const mrna = gene.get('subfeatures')[0];
|
|
69
|
+
expect(geneLikeRoot(mrna)).toBe(gene);
|
|
70
|
+
});
|
|
71
|
+
it('stays put with no gene-like parent', () => {
|
|
72
|
+
const mrna = feature('mRNA', [feature('CDS')]);
|
|
73
|
+
expect(geneLikeRoot(mrna)).toBe(mrna);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
declare const BlastAutomaticPanel: ({ handleClose, feature, model, children, }: {
|
|
3
|
+
declare const BlastAutomaticPanel: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
|
|
4
4
|
model: AbstractTrackModel;
|
|
5
5
|
feature: Feature;
|
|
6
6
|
handleClose: () => void;
|
|
7
7
|
children: React.ReactNode;
|
|
8
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
9
|
+
preferredTranscriptId?: string;
|
|
8
10
|
}) => React.JSX.Element;
|
|
9
11
|
export default BlastAutomaticPanel;
|
|
@@ -6,24 +6,24 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
6
6
|
import TextField2 from '../../../components/TextField2';
|
|
7
7
|
import { getBlastViewTitle, getGeneIdentifiers, getLinearGenomeView, } from '../../util';
|
|
8
8
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
9
|
+
import SequenceStatusMessage from '../SequenceStatus';
|
|
9
10
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
10
11
|
import TranscriptSelector from '../TranscriptSelector';
|
|
11
12
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
12
13
|
import CachedBlastResults from './CachedBlastResults';
|
|
13
14
|
import MsaAlgorithmSelect from './MsaAlgorithmSelect';
|
|
14
15
|
import { blastLaunchView } from './blastLaunchView';
|
|
15
|
-
import { databaseOptionsFor, defaultSearchFor, searchPrograms } from './consts';
|
|
16
|
+
import { databaseLabel, databaseOptionsFor, defaultMaxHits, defaultSearchFor, searchProgramLabels, searchPrograms, } from './consts';
|
|
16
17
|
import { useStoredMsaAlgorithm, useStoredSearchChoice, } from './searchChoiceStorage';
|
|
17
18
|
import { useCachedBlastResults } from './useCachedBlastResults';
|
|
18
19
|
const useStyles = makeStyles()({
|
|
19
20
|
selectField: {
|
|
20
21
|
width: 150,
|
|
21
22
|
},
|
|
22
|
-
// wider than the rest because the
|
|
23
|
-
//
|
|
24
|
-
// `uniprotkb_swis…`, which does not distinguish it from `uniprotkb_trembl`
|
|
23
|
+
// wider than the rest because the collection names are what the user came to
|
|
24
|
+
// read, and truncating them does not distinguish Swiss-Prot from TrEMBL
|
|
25
25
|
databaseField: {
|
|
26
|
-
width:
|
|
26
|
+
width: 260,
|
|
27
27
|
},
|
|
28
28
|
cachedResultsAccordion: {
|
|
29
29
|
marginTop: 20,
|
|
@@ -32,7 +32,7 @@ const useStyles = makeStyles()({
|
|
|
32
32
|
marginTop: 20,
|
|
33
33
|
},
|
|
34
34
|
});
|
|
35
|
-
const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, children, }) {
|
|
35
|
+
const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
|
|
36
36
|
const { classes } = useStyles();
|
|
37
37
|
const view = getLinearGenomeView(model);
|
|
38
38
|
const [launchViewError, setLaunchViewError] = useState();
|
|
@@ -41,11 +41,18 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
41
41
|
// would let them drift into that
|
|
42
42
|
const [search, setSearch] = useStoredSearchChoice();
|
|
43
43
|
const [selectedMsaAlgorithm, setSelectedMsaAlgorithm] = useStoredMsaAlgorithm();
|
|
44
|
+
const [maxHits, setMaxHits] = useState(String(defaultMaxHits));
|
|
45
|
+
const hitCount = Number(maxHits);
|
|
46
|
+
const hitCountValid = Number.isInteger(hitCount) && hitCount >= 1;
|
|
44
47
|
const isPhmmer = search.program === 'phmmer';
|
|
45
48
|
const geneIds = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
46
49
|
const { results: cachedResults, error: cachedResultsError } = useCachedBlastResults(geneIds);
|
|
47
|
-
const transcriptSelection = useTranscriptSelection({
|
|
48
|
-
|
|
50
|
+
const transcriptSelection = useTranscriptSelection({
|
|
51
|
+
feature,
|
|
52
|
+
view,
|
|
53
|
+
preferredTranscriptId,
|
|
54
|
+
});
|
|
55
|
+
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
49
56
|
const e = transcriptSelection.error ?? launchViewError ?? cachedResultsError;
|
|
50
57
|
return (React.createElement(React.Fragment, null,
|
|
51
58
|
React.createElement(LaunchPanelContent, { error: e },
|
|
@@ -55,36 +62,30 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
55
62
|
// program replaces the database rather than keeping a name the new
|
|
56
63
|
// one has never heard of
|
|
57
64
|
setSearch(defaultSearchFor(event.target.value));
|
|
58
|
-
} }, searchPrograms.map(val => (React.createElement(MenuItem, { value: val, key: val }, val)))),
|
|
65
|
+
} }, searchPrograms.map(val => (React.createElement(MenuItem, { value: val, key: val }, searchProgramLabels[val])))),
|
|
59
66
|
React.createElement(TextField2, { variant: "outlined", label: "Database", className: classes.databaseField, select: true, value: search.database, onChange: event => {
|
|
60
67
|
setSearch({
|
|
61
68
|
program: search.program,
|
|
62
69
|
database: event.target.value,
|
|
63
70
|
});
|
|
64
|
-
} }, databaseOptionsFor(search.program).map(val => (React.createElement(MenuItem, { value: val, key: val }, val)))),
|
|
71
|
+
} }, databaseOptionsFor(search.program).map(val => (React.createElement(MenuItem, { value: val, key: val }, databaseLabel(val))))),
|
|
65
72
|
isPhmmer ? null : (React.createElement(MsaAlgorithmSelect, { className: classes.selectField, value: selectedMsaAlgorithm, onChange: setSelectedMsaAlgorithm })),
|
|
73
|
+
React.createElement(TextField2, { variant: "outlined", label: "Hits", className: classes.selectField, type: "number", value: maxHits, onChange: event => {
|
|
74
|
+
setMaxHits(event.target.value);
|
|
75
|
+
}, error: !hitCountValid, helperText: "best-scoring sequences to keep" }),
|
|
66
76
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
67
|
-
React.createElement(Typography, { className: classes.infoText },
|
|
77
|
+
React.createElement(Typography, { variant: "body2", className: classes.infoText },
|
|
68
78
|
isPhmmer
|
|
69
|
-
?
|
|
70
|
-
|
|
71
|
-
directly — nothing is realigned afterwards. The tree is then built
|
|
72
|
-
from it by neighbour-joining. A hit matching the query in more
|
|
73
|
-
than one place appears once per matched region.`
|
|
74
|
-
: `This panel will automatically submit a blastp query to EBI, which
|
|
75
|
-
searches UniProtKB. Searches usually finish in under a minute, and
|
|
76
|
-
swissprot returns curated sequences that align more cleanly than
|
|
77
|
-
the many near-identical entries a TrEMBL search brings back. After
|
|
78
|
-
completion, all the hits will be run through a multiple sequence
|
|
79
|
-
alignment.`,
|
|
79
|
+
? 'phmmer aligns the hits as it finds them, so nothing is realigned afterwards.'
|
|
80
|
+
: 'The hits come back from EBI and are then run through the chosen aligner.',
|
|
80
81
|
' ',
|
|
81
|
-
"
|
|
82
|
+
"The EBI queue is the wait, and it runs from seconds to many minutes."),
|
|
82
83
|
cachedResults.length > 0 ? (React.createElement(Accordion, { className: classes.cachedResultsAccordion },
|
|
83
84
|
React.createElement(AccordionSummary, { expandIcon: React.createElement(ExpandMoreIcon, null) },
|
|
84
85
|
React.createElement(Typography, null, "Previous BLAST Results")),
|
|
85
86
|
React.createElement(AccordionDetails, null,
|
|
86
87
|
React.createElement(CachedBlastResults, { model: model, handleClose: handleClose, feature: feature })))) : null),
|
|
87
|
-
React.createElement(SubmitCancelActions, { model: model, submitDisabled: !proteinSequence, onSubmit: () => {
|
|
88
|
+
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !hitCountValid, onSubmit: () => {
|
|
88
89
|
try {
|
|
89
90
|
if (selectedTranscript) {
|
|
90
91
|
setLaunchViewError(undefined);
|
|
@@ -96,14 +97,16 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
|
|
|
96
97
|
? {
|
|
97
98
|
searchProgram: 'phmmer',
|
|
98
99
|
blastDatabase: search.database,
|
|
99
|
-
|
|
100
|
+
maxHits: hitCount,
|
|
101
|
+
selectedTranscript: selectedTranscript.toJSON(),
|
|
100
102
|
proteinSequence,
|
|
101
103
|
}
|
|
102
104
|
: {
|
|
103
105
|
searchProgram: 'blastp',
|
|
104
106
|
blastDatabase: search.database,
|
|
105
107
|
msaAlgorithm: selectedMsaAlgorithm,
|
|
106
|
-
|
|
108
|
+
maxHits: hitCount,
|
|
109
|
+
selectedTranscript: selectedTranscript.toJSON(),
|
|
107
110
|
proteinSequence,
|
|
108
111
|
},
|
|
109
112
|
});
|
|
@@ -11,10 +11,12 @@ import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
|
11
11
|
* button -- leaving them to find the Manual upload tab, re-pick the transcript
|
|
12
12
|
* they had already chosen here, and hand-type the row name.
|
|
13
13
|
*/
|
|
14
|
-
declare const BlastManualPanel: ({ handleClose, feature, model, children, }: {
|
|
14
|
+
declare const BlastManualPanel: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
model: AbstractTrackModel;
|
|
17
17
|
feature: Feature;
|
|
18
18
|
handleClose: () => void;
|
|
19
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
20
|
+
preferredTranscriptId?: string;
|
|
19
21
|
}) => React.JSX.Element;
|
|
20
22
|
export default BlastManualPanel;
|
|
@@ -43,13 +43,17 @@ const useStyles = makeStyles()({
|
|
|
43
43
|
* button -- leaving them to find the Manual upload tab, re-pick the transcript
|
|
44
44
|
* they had already chosen here, and hand-type the row name.
|
|
45
45
|
*/
|
|
46
|
-
const BlastManualPanel = observer(function ({ handleClose, feature, model, children, }) {
|
|
46
|
+
const BlastManualPanel = observer(function ({ handleClose, feature, model, children, preferredTranscriptId, }) {
|
|
47
47
|
const { classes } = useStyles();
|
|
48
48
|
const view = getLinearGenomeView(model);
|
|
49
49
|
const [launchViewError, setLaunchViewError] = useState();
|
|
50
50
|
const [msaText, setMsaText] = useState('');
|
|
51
51
|
const [treeText, setTreeText] = useState('');
|
|
52
|
-
const transcriptSelection = useTranscriptSelection({
|
|
52
|
+
const transcriptSelection = useTranscriptSelection({
|
|
53
|
+
feature,
|
|
54
|
+
view,
|
|
55
|
+
preferredTranscriptId,
|
|
56
|
+
});
|
|
53
57
|
const { proteinSequence, selectedTranscript, error } = transcriptSelection;
|
|
54
58
|
const queryRow = useQueryRowName(msaText, proteinSequence);
|
|
55
59
|
const s2 = cleanProteinSequence(proteinSequence);
|
|
@@ -88,6 +92,7 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
|
|
|
88
92
|
view,
|
|
89
93
|
feature: selectedTranscript,
|
|
90
94
|
querySeqName: queryRow.querySeqName,
|
|
95
|
+
querySeqOffset: queryRow.querySeqOffset,
|
|
91
96
|
data: { msa: msaText, tree: treeText },
|
|
92
97
|
});
|
|
93
98
|
handleClose();
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
3
|
declare const panelMap: {
|
|
4
|
-
readonly automatic: ({ handleClose, feature, model, children, }: {
|
|
4
|
+
readonly automatic: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
|
|
5
5
|
model: AbstractTrackModel;
|
|
6
6
|
feature: Feature;
|
|
7
7
|
handleClose: () => void;
|
|
8
8
|
children: React.ReactNode;
|
|
9
|
+
preferredTranscriptId?: string;
|
|
9
10
|
}) => React.JSX.Element;
|
|
10
|
-
readonly manual: ({ handleClose, feature, model, children, }: {
|
|
11
|
+
readonly manual: ({ handleClose, feature, model, children, preferredTranscriptId, }: {
|
|
11
12
|
children: React.ReactNode;
|
|
12
13
|
model: AbstractTrackModel;
|
|
13
14
|
feature: Feature;
|
|
14
15
|
handleClose: () => void;
|
|
16
|
+
preferredTranscriptId?: string;
|
|
15
17
|
}) => React.JSX.Element;
|
|
16
18
|
};
|
|
17
19
|
export type BlastLookupMethod = keyof typeof panelMap;
|
|
18
|
-
export default function BlastPanel({ handleClose, model, feature, }: {
|
|
20
|
+
export default function BlastPanel({ handleClose, model, feature, preferredTranscriptId, }: {
|
|
19
21
|
handleClose: () => void;
|
|
20
22
|
model: AbstractTrackModel;
|
|
21
23
|
feature: Feature;
|
|
24
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
25
|
+
preferredTranscriptId?: string;
|
|
22
26
|
}): React.JSX.Element;
|
|
23
27
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import SettingsIcon from '@mui/icons-material/Settings';
|
|
3
|
-
import { IconButton } from '@mui/material';
|
|
3
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
import { DEFAULT_EBI_EMAIL, EBI_EMAIL_STORAGE_KEY, } from '../../../utils/ebiJobDispatcher';
|
|
6
6
|
import { useLocalStorage } from '../../../utils/useLocalStorage';
|
|
@@ -17,18 +17,19 @@ const panelMap = {
|
|
|
17
17
|
automatic: BlastAutomaticPanel,
|
|
18
18
|
manual: BlastManualPanel,
|
|
19
19
|
};
|
|
20
|
-
export default function BlastPanel({ handleClose, model, feature, }) {
|
|
20
|
+
export default function BlastPanel({ handleClose, model, feature, preferredTranscriptId, }) {
|
|
21
21
|
const [lookupMethod, setLookupMethod] = useState('automatic');
|
|
22
22
|
const [ebiEmail, setEbiEmail] = useLocalStorage(EBI_EMAIL_STORAGE_KEY, DEFAULT_EBI_EMAIL);
|
|
23
23
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
24
24
|
const { classes } = useStyles();
|
|
25
25
|
const Panel = panelMap[lookupMethod];
|
|
26
26
|
return (React.createElement(React.Fragment, null,
|
|
27
|
-
React.createElement(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
React.createElement(Tooltip, { title: `EBI settings — jobs are submitted with ${ebiEmail}` },
|
|
28
|
+
React.createElement(IconButton, { className: classes.settingsButton, "aria-label": "EBI settings", size: "small", onClick: () => {
|
|
29
|
+
setSettingsOpen(true);
|
|
30
|
+
} },
|
|
31
|
+
React.createElement(SettingsIcon, null))),
|
|
32
|
+
React.createElement(Panel, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId },
|
|
32
33
|
React.createElement(BlastMethodSelector, { lookupMethod: lookupMethod, setLookupMethod: setLookupMethod })),
|
|
33
34
|
settingsOpen ? (React.createElement(BlastSettingsDialog, { ebiEmail: ebiEmail, handleClose: settings => {
|
|
34
35
|
if (settings) {
|
|
@@ -17,9 +17,9 @@ export default function BlastSettingsDialog({ handleClose, ebiEmail, }) {
|
|
|
17
17
|
return (React.createElement(Dialog, { open: true, maxWidth: "lg", onClose: () => {
|
|
18
18
|
handleClose();
|
|
19
19
|
} },
|
|
20
|
-
React.createElement(DialogTitle, null, "
|
|
20
|
+
React.createElement(DialogTitle, null, "EBI settings"),
|
|
21
21
|
React.createElement(DialogContent, null,
|
|
22
|
-
React.createElement(Typography, { variant: "subtitle2", className: classes.help }, "
|
|
22
|
+
React.createElement(Typography, { variant: "subtitle2", className: classes.help }, "Every search this tab runs is submitted to EBI with this address, which is how they reach whoever is generating the load. If your site sends real volume, use your own."),
|
|
23
23
|
React.createElement(TextField2, { autoFocus: true, margin: "dense", label: "EBI contact email", fullWidth: true, variant: "outlined", value: tempEbiEmail, className: classes.field, onChange: e => {
|
|
24
24
|
setTempEbiEmail(e.target.value);
|
|
25
25
|
} }),
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { MenuItem } from '@mui/material';
|
|
3
3
|
import TextField2 from '../../../components/TextField2';
|
|
4
|
-
import { msaAlgorithms } from './consts';
|
|
4
|
+
import { msaAlgorithmLabels, msaAlgorithms } from './consts';
|
|
5
5
|
export default function MsaAlgorithmSelect({ value, onChange, className, }) {
|
|
6
|
-
return (React.createElement(TextField2, { variant: "outlined", label: "MSA Algorithm", className: className, select: true, value: value,
|
|
6
|
+
return (React.createElement(TextField2, { variant: "outlined", label: "MSA Algorithm", className: className, select: true, value: value, helperText: value === 'browser'
|
|
7
|
+
? 'no EBI job; rows aligned to the query'
|
|
8
|
+
: undefined, onChange: event => {
|
|
7
9
|
onChange(event.target.value);
|
|
8
|
-
} }, msaAlgorithms.map(val => (React.createElement(MenuItem, { value: val, key: val }, val)))));
|
|
10
|
+
} }, msaAlgorithms.map(val => (React.createElement(MenuItem, { value: val, key: val }, msaAlgorithmLabels[val])))));
|
|
9
11
|
}
|
|
@@ -5,8 +5,20 @@
|
|
|
5
5
|
* cannot read a response from it at all. See docs/blast.md.
|
|
6
6
|
*/
|
|
7
7
|
export declare const BASE_BLAST_URL = "https://blast.ncbi.nlm.nih.gov/Blast.cgi";
|
|
8
|
-
|
|
8
|
+
/** The aligners EBI's Job Dispatcher runs, each a tool name at their REST api. */
|
|
9
|
+
export declare const ebiMsaAlgorithms: readonly ["clustalo", "muscle", "kalign", "mafft"];
|
|
10
|
+
export type EbiMsaAlgorithm = (typeof ebiMsaAlgorithms)[number];
|
|
11
|
+
/**
|
|
12
|
+
* `browser` is no job at all: each sequence is aligned to the query in the
|
|
13
|
+
* page and the rows merged on the query (utils/browserAlign.ts), with the tree
|
|
14
|
+
* built by react-msaview's neighbour joining. It is the aligner for a launch
|
|
15
|
+
* that must not depend on EBI, and for one that wants to be quick -- a
|
|
16
|
+
* hundred rows take a second or two against a Job Dispatcher queue that has
|
|
17
|
+
* been measured at anything from ten seconds to fifteen minutes.
|
|
18
|
+
*/
|
|
19
|
+
export declare const msaAlgorithms: readonly ["clustalo", "muscle", "kalign", "mafft", "browser"];
|
|
9
20
|
export type MsaAlgorithm = (typeof msaAlgorithms)[number];
|
|
21
|
+
export declare const msaAlgorithmLabels: Record<MsaAlgorithm, string>;
|
|
10
22
|
/**
|
|
11
23
|
* EBI rejects a submission naming a database outside its own list with a 400,
|
|
12
24
|
* so every value here has to appear in
|
|
@@ -19,15 +31,24 @@ export type BlastDatabase = (typeof blastDatabaseOptions)[number];
|
|
|
19
31
|
export declare const defaultBlastDatabase: BlastDatabase;
|
|
20
32
|
export declare const searchPrograms: readonly ["blastp", "phmmer"];
|
|
21
33
|
export type SearchProgram = (typeof searchPrograms)[number];
|
|
34
|
+
export declare const searchProgramLabels: Record<SearchProgram, string>;
|
|
22
35
|
/**
|
|
23
36
|
* phmmer offers PDB, AlphaFold, Ensembl Genomes, MEROPS and ChEMBL too, but
|
|
24
|
-
* targets outside UniProt carry no
|
|
37
|
+
* targets outside UniProt carry no species in their description, so those rows
|
|
25
38
|
* would lose their species and common name. Only the databases that label their
|
|
26
39
|
* hits are offered.
|
|
40
|
+
*
|
|
41
|
+
* rp15..rp75 are the Representative Proteomes: UniProt's reference proteomes
|
|
42
|
+
* thinned so that no two are more than 15% (35%, 55%, 75%) similar, which is
|
|
43
|
+
* the widest taxonomic spread per hit that any of these databases gives. rp15
|
|
44
|
+
* is the one to reach for when the question is "what is this like across all
|
|
45
|
+
* of life"; swissprot when it is "what is this like in the curated set".
|
|
27
46
|
*/
|
|
28
|
-
export declare const phmmerDatabaseOptions: readonly ["swissprot", "uniprotkb", "uniprotrefprot"];
|
|
47
|
+
export declare const phmmerDatabaseOptions: readonly ["swissprot", "uniprotkb", "uniprotrefprot", "rp75", "rp55", "rp35", "rp15"];
|
|
29
48
|
export type PhmmerDatabase = (typeof phmmerDatabaseOptions)[number];
|
|
30
49
|
export declare const defaultPhmmerDatabase: PhmmerDatabase;
|
|
50
|
+
export declare const defaultMaxHits = 100;
|
|
51
|
+
export declare function snapBlastHitCount(maxHits: number): number;
|
|
31
52
|
/**
|
|
32
53
|
* A program together with a database that program actually has.
|
|
33
54
|
*
|
|
@@ -44,4 +65,5 @@ export type SearchChoice = {
|
|
|
44
65
|
database: PhmmerDatabase;
|
|
45
66
|
};
|
|
46
67
|
export declare function defaultSearchFor(program: SearchProgram): SearchChoice;
|
|
47
|
-
export declare function databaseOptionsFor(program: SearchProgram): readonly ["uniprotkb_swissprot", "uniprotkb", "pan_proteomes", "uniprotkb_trembl"] | readonly ["swissprot", "uniprotkb", "uniprotrefprot"];
|
|
68
|
+
export declare function databaseOptionsFor(program: SearchProgram): readonly ["uniprotkb_swissprot", "uniprotkb", "pan_proteomes", "uniprotkb_trembl"] | readonly ["swissprot", "uniprotkb", "uniprotrefprot", "rp75", "rp55", "rp35", "rp15"];
|
|
69
|
+
export declare function databaseLabel(database: BlastDatabase | PhmmerDatabase): string;
|
|
@@ -5,7 +5,29 @@
|
|
|
5
5
|
* cannot read a response from it at all. See docs/blast.md.
|
|
6
6
|
*/
|
|
7
7
|
export const BASE_BLAST_URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi';
|
|
8
|
-
|
|
8
|
+
/** The aligners EBI's Job Dispatcher runs, each a tool name at their REST api. */
|
|
9
|
+
export const ebiMsaAlgorithms = [
|
|
10
|
+
'clustalo',
|
|
11
|
+
'muscle',
|
|
12
|
+
'kalign',
|
|
13
|
+
'mafft',
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* `browser` is no job at all: each sequence is aligned to the query in the
|
|
17
|
+
* page and the rows merged on the query (utils/browserAlign.ts), with the tree
|
|
18
|
+
* built by react-msaview's neighbour joining. It is the aligner for a launch
|
|
19
|
+
* that must not depend on EBI, and for one that wants to be quick -- a
|
|
20
|
+
* hundred rows take a second or two against a Job Dispatcher queue that has
|
|
21
|
+
* been measured at anything from ten seconds to fifteen minutes.
|
|
22
|
+
*/
|
|
23
|
+
export const msaAlgorithms = [...ebiMsaAlgorithms, 'browser'];
|
|
24
|
+
export const msaAlgorithmLabels = {
|
|
25
|
+
clustalo: 'Clustal Omega (EBI)',
|
|
26
|
+
muscle: 'MUSCLE (EBI)',
|
|
27
|
+
kalign: 'Kalign (EBI)',
|
|
28
|
+
mafft: 'MAFFT (EBI)',
|
|
29
|
+
browser: 'in browser, query-anchored',
|
|
30
|
+
};
|
|
9
31
|
/**
|
|
10
32
|
* EBI rejects a submission naming a database outside its own list with a 400,
|
|
11
33
|
* so every value here has to appear in
|
|
@@ -23,18 +45,42 @@ export const blastDatabaseOptions = [
|
|
|
23
45
|
// many near-identical TrEMBL entries an alignment reads poorly
|
|
24
46
|
export const defaultBlastDatabase = 'uniprotkb_swissprot';
|
|
25
47
|
export const searchPrograms = ['blastp', 'phmmer'];
|
|
48
|
+
export const searchProgramLabels = {
|
|
49
|
+
blastp: 'blastp (sequence search)',
|
|
50
|
+
phmmer: 'phmmer (profile HMM search)',
|
|
51
|
+
};
|
|
26
52
|
/**
|
|
27
53
|
* phmmer offers PDB, AlphaFold, Ensembl Genomes, MEROPS and ChEMBL too, but
|
|
28
|
-
* targets outside UniProt carry no
|
|
54
|
+
* targets outside UniProt carry no species in their description, so those rows
|
|
29
55
|
* would lose their species and common name. Only the databases that label their
|
|
30
56
|
* hits are offered.
|
|
57
|
+
*
|
|
58
|
+
* rp15..rp75 are the Representative Proteomes: UniProt's reference proteomes
|
|
59
|
+
* thinned so that no two are more than 15% (35%, 55%, 75%) similar, which is
|
|
60
|
+
* the widest taxonomic spread per hit that any of these databases gives. rp15
|
|
61
|
+
* is the one to reach for when the question is "what is this like across all
|
|
62
|
+
* of life"; swissprot when it is "what is this like in the curated set".
|
|
31
63
|
*/
|
|
32
64
|
export const phmmerDatabaseOptions = [
|
|
33
65
|
'swissprot',
|
|
34
66
|
'uniprotkb',
|
|
35
67
|
'uniprotrefprot',
|
|
68
|
+
'rp75',
|
|
69
|
+
'rp55',
|
|
70
|
+
'rp35',
|
|
71
|
+
'rp15',
|
|
36
72
|
];
|
|
37
73
|
export const defaultPhmmerDatabase = 'swissprot';
|
|
74
|
+
/**
|
|
75
|
+
* The hit counts EBI's ncbiblast accepts for `alignments` and `scores`. A value
|
|
76
|
+
* off this list is a 400 at submit time, so a request is rounded up to the next
|
|
77
|
+
* one on it.
|
|
78
|
+
*/
|
|
79
|
+
const blastHitCounts = [5, 10, 20, 50, 100, 150, 200, 250, 500, 750, 1000];
|
|
80
|
+
export const defaultMaxHits = 100;
|
|
81
|
+
export function snapBlastHitCount(maxHits) {
|
|
82
|
+
return blastHitCounts.find(n => n >= maxHits) ?? blastHitCounts.at(-1);
|
|
83
|
+
}
|
|
38
84
|
export function defaultSearchFor(program) {
|
|
39
85
|
return program === 'phmmer'
|
|
40
86
|
? { program, database: defaultPhmmerDatabase }
|
|
@@ -43,3 +89,23 @@ export function defaultSearchFor(program) {
|
|
|
43
89
|
export function databaseOptionsFor(program) {
|
|
44
90
|
return program === 'phmmer' ? phmmerDatabaseOptions : blastDatabaseOptions;
|
|
45
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* What each database is called in the menu. The keys are what EBI is sent and
|
|
94
|
+
* are not negotiable; `uniprotkb_swissprot` and `swissprot` are the same
|
|
95
|
+
* collection under each service's own name, so they read the same here.
|
|
96
|
+
*/
|
|
97
|
+
const databaseLabels = {
|
|
98
|
+
uniprotkb_swissprot: 'UniProtKB/Swiss-Prot (curated)',
|
|
99
|
+
swissprot: 'UniProtKB/Swiss-Prot (curated)',
|
|
100
|
+
uniprotkb: 'UniProtKB (all entries)',
|
|
101
|
+
uniprotkb_trembl: 'UniProtKB/TrEMBL (unreviewed)',
|
|
102
|
+
pan_proteomes: 'Pan-proteomes',
|
|
103
|
+
uniprotrefprot: 'UniProt reference proteomes',
|
|
104
|
+
rp75: 'Representative proteomes, 75%',
|
|
105
|
+
rp55: 'Representative proteomes, 55%',
|
|
106
|
+
rp35: 'Representative proteomes, 35%',
|
|
107
|
+
rp15: 'Representative proteomes, 15% (widest spread)',
|
|
108
|
+
};
|
|
109
|
+
export function databaseLabel(database) {
|
|
110
|
+
return databaseLabels[database];
|
|
111
|
+
}
|