jbrowse-plugin-msaview 2.8.2 → 2.10.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.
Files changed (108) hide show
  1. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastRIDPanel.d.ts → BlastQuery/BlastAutomaticPanel.d.ts} +2 -3
  2. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastAutomaticPanel.js → BlastQuery/BlastAutomaticPanel.js} +6 -25
  3. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastManualPanel.d.ts → BlastQuery/BlastManualPanel.d.ts} +2 -3
  4. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastManualPanel.js → BlastQuery/BlastManualPanel.js} +7 -4
  5. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastMethodSelector.d.ts → BlastQuery/BlastMethodSelector.d.ts} +2 -2
  6. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastMethodSelector.js → BlastQuery/BlastMethodSelector.js} +1 -2
  7. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastPanel.d.ts → BlastQuery/BlastPanel.d.ts} +3 -12
  8. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastPanel.js → BlastQuery/BlastPanel.js} +14 -16
  9. package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.d.ts +8 -0
  10. package/dist/LaunchMsaView/components/{NCBIBlastQuery/NCBISettingsDialog.js → BlastQuery/BlastSettingsDialog.js} +13 -9
  11. package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/CachedBlastResults.js +1 -1
  12. package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +12 -0
  13. package/dist/LaunchMsaView/components/BlastQuery/consts.js +17 -0
  14. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +3 -3
  15. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +15 -35
  16. package/dist/LaunchMsaView/components/OrthologQuery/QuerySpeciesSelect.d.ts +15 -0
  17. package/dist/LaunchMsaView/components/OrthologQuery/QuerySpeciesSelect.js +67 -0
  18. package/dist/LaunchMsaView/index.js +19 -1
  19. package/dist/MsaViewPanel/components/JobLink.d.ts +5 -0
  20. package/dist/MsaViewPanel/components/JobLink.js +13 -0
  21. package/dist/MsaViewPanel/components/LoadingBLAST.d.ts +1 -2
  22. package/dist/MsaViewPanel/components/LoadingBLAST.js +8 -15
  23. package/dist/MsaViewPanel/components/MsaViewPanel.js +1 -1
  24. package/dist/MsaViewPanel/doLaunchBlast.js +11 -19
  25. package/dist/MsaViewPanel/doLaunchOrthologs.js +43 -12
  26. package/dist/MsaViewPanel/doLaunchOrthologs.test.d.ts +1 -0
  27. package/dist/MsaViewPanel/doLaunchOrthologs.test.js +201 -0
  28. package/dist/MsaViewPanel/model.d.ts +20 -12
  29. package/dist/jbrowse-plugin-msaview.umd.production.min.js +31 -33
  30. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  31. package/dist/utils/blastCache.d.ts +8 -4
  32. package/dist/utils/blastCache.js +4 -5
  33. package/dist/utils/ebiBlast.d.ts +52 -0
  34. package/dist/utils/ebiBlast.js +63 -0
  35. package/dist/utils/ebiJobDispatcher.d.ts +33 -0
  36. package/dist/utils/ebiJobDispatcher.js +80 -0
  37. package/dist/utils/ebiJobDispatcher.test.d.ts +1 -0
  38. package/dist/utils/ebiJobDispatcher.test.js +46 -0
  39. package/dist/utils/eutils.d.ts +9 -0
  40. package/dist/utils/eutils.js +18 -0
  41. package/dist/utils/fetch.js +24 -1
  42. package/dist/utils/msa.d.ts +1 -1
  43. package/dist/utils/msa.js +25 -32
  44. package/dist/utils/ncbiOrthologs.d.ts +17 -75
  45. package/dist/utils/ncbiOrthologs.js +67 -63
  46. package/dist/utils/ncbiOrthologs.test.js +82 -2
  47. package/dist/utils/ncbiTaxonomy.d.ts +11 -0
  48. package/dist/utils/ncbiTaxonomy.js +33 -0
  49. package/dist/utils/types.d.ts +9 -14
  50. package/dist/utils/useLocalStorage.d.ts +1 -0
  51. package/dist/utils/useLocalStorage.js +1 -1
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/package.json +3 -3
  55. package/src/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx → BlastQuery/BlastAutomaticPanel.tsx} +13 -59
  56. package/src/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastManualPanel.tsx → BlastQuery/BlastManualPanel.tsx} +7 -6
  57. package/src/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastMethodSelector.tsx → BlastQuery/BlastMethodSelector.tsx} +2 -7
  58. package/src/LaunchMsaView/components/{NCBIBlastQuery/NCBIBlastPanel.tsx → BlastQuery/BlastPanel.tsx} +21 -25
  59. package/src/LaunchMsaView/components/{NCBIBlastQuery/NCBISettingsDialog.tsx → BlastQuery/BlastSettingsDialog.tsx} +26 -13
  60. package/src/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/CachedBlastResults.tsx +1 -1
  61. package/src/LaunchMsaView/components/BlastQuery/consts.ts +22 -0
  62. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +5 -7
  63. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +35 -73
  64. package/src/LaunchMsaView/components/OrthologQuery/QuerySpeciesSelect.tsx +92 -0
  65. package/src/LaunchMsaView/index.ts +20 -2
  66. package/src/MsaViewPanel/components/JobLink.tsx +17 -0
  67. package/src/MsaViewPanel/components/LoadingBLAST.tsx +10 -41
  68. package/src/MsaViewPanel/components/MsaViewPanel.tsx +1 -1
  69. package/src/MsaViewPanel/doLaunchBlast.ts +12 -28
  70. package/src/MsaViewPanel/doLaunchOrthologs.test.ts +247 -0
  71. package/src/MsaViewPanel/doLaunchOrthologs.ts +56 -13
  72. package/src/MsaViewPanel/model.ts +10 -7
  73. package/src/utils/blastCache.ts +8 -9
  74. package/src/utils/ebiBlast.ts +114 -0
  75. package/src/utils/ebiJobDispatcher.test.ts +54 -0
  76. package/src/utils/ebiJobDispatcher.ts +120 -0
  77. package/src/utils/eutils.ts +19 -0
  78. package/src/utils/fetch.ts +26 -1
  79. package/src/utils/msa.ts +26 -47
  80. package/src/utils/ncbiOrthologs.test.ts +96 -2
  81. package/src/utils/ncbiOrthologs.ts +83 -71
  82. package/src/utils/ncbiTaxonomy.ts +37 -0
  83. package/src/utils/types.ts +8 -13
  84. package/src/utils/useLocalStorage.ts +1 -1
  85. package/src/version.ts +1 -1
  86. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.d.ts +0 -10
  87. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.js +0 -74
  88. package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBISettingsDialog.d.ts +0 -5
  89. package/dist/LaunchMsaView/components/NCBIBlastQuery/consts.d.ts +0 -7
  90. package/dist/LaunchMsaView/components/NCBIBlastQuery/consts.js +0 -4
  91. package/dist/MsaViewPanel/components/RIDLink.d.ts +0 -6
  92. package/dist/MsaViewPanel/components/RIDLink.js +0 -12
  93. package/dist/utils/ncbiBlast.d.ts +0 -30
  94. package/dist/utils/ncbiBlast.js +0 -84
  95. package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastRIDPanel.tsx +0 -137
  96. package/src/LaunchMsaView/components/NCBIBlastQuery/consts.ts +0 -10
  97. package/src/MsaViewPanel/components/RIDLink.tsx +0 -19
  98. package/src/utils/ncbiBlast.ts +0 -143
  99. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/CachedBlastResults.d.ts +0 -0
  100. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/MsaAlgorithmSelect.d.ts +0 -0
  101. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/MsaAlgorithmSelect.js +0 -0
  102. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/blastLaunchView.d.ts +0 -0
  103. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/blastLaunchView.js +0 -0
  104. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/useCachedBlastResults.d.ts +0 -0
  105. /package/dist/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/useCachedBlastResults.js +0 -0
  106. /package/src/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/MsaAlgorithmSelect.tsx +0 -0
  107. /package/src/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/blastLaunchView.ts +0 -0
  108. /package/src/LaunchMsaView/components/{NCBIBlastQuery → BlastQuery}/useCachedBlastResults.ts +0 -0
@@ -0,0 +1,37 @@
1
+ import { NCBI_EMAIL, NCBI_TOOL } from './eutils'
2
+ import { jsonfetch } from './fetch'
3
+
4
+ const EUTILS = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils'
5
+
6
+ /**
7
+ * Free text -> NCBI taxon id. A bare number is taken as the id itself; anything
8
+ * else is searched against db=taxonomy, which resolves a scientific name
9
+ * (`Danio rerio`), a common name (`zebrafish`) and a genus alike.
10
+ *
11
+ * This replaces a fixed list of species the dialog used to offer. The query
12
+ * taxon has to match the assembly the user is browsing -- `resolveGeneId`
13
+ * searches `SYMBOL[Gene Name] AND <taxid>[taxid]` -- so a list that stops at 23
14
+ * species silently resolves the wrong organism's gene for anyone outside it.
15
+ */
16
+ export async function resolveTaxId(query: string) {
17
+ const term = query.trim()
18
+ if (!term) {
19
+ return undefined
20
+ }
21
+ if (/^\d+$/.test(term)) {
22
+ return Number(term)
23
+ }
24
+ const search = new URLSearchParams({
25
+ db: 'taxonomy',
26
+ term,
27
+ retmode: 'json',
28
+ retmax: '1',
29
+ tool: NCBI_TOOL,
30
+ email: NCBI_EMAIL,
31
+ })
32
+ const json = await jsonfetch<{ esearchresult?: { idlist?: string[] } }>(
33
+ `${EUTILS}/esearch.fcgi?${search.toString()}`,
34
+ )
35
+ const id = json.esearchresult?.idlist?.[0]
36
+ return id ? Number(id) : undefined
37
+ }
@@ -6,17 +6,12 @@ export interface BlastHitDescription {
6
6
  title?: string
7
7
  }
8
8
 
9
- export interface BlastResults {
10
- BlastOutput2: {
11
- report: {
12
- results: {
13
- search: {
14
- hits: {
15
- description: BlastHitDescription[]
16
- hsps: { hseq: string }[]
17
- }[]
18
- }
19
- }
20
- }
21
- }[]
9
+ /**
10
+ * The shape utils/ebiBlast.ts normalizes EBI's hits into. It keeps the field
11
+ * names NCBI's JSON2 used, so everything downstream of the search — row naming,
12
+ * taxonomy lookup, the MSA — was left untouched when the backend moved.
13
+ */
14
+ export interface BlastHit {
15
+ description: BlastHitDescription[]
16
+ hsps: { hseq: string }[]
22
17
  }
@@ -5,7 +5,7 @@ import { useState } from 'react'
5
5
  // throw "(0, PR.useLocalStorage) is not a function" on hosts built during that
6
6
  // window. Same failure mode as `defaultCodonTable`; keeping our own copy takes
7
7
  // this plugin out of the whack-a-mole.
8
- function readLocalStorage<T>(key: string, initialValue: T): T {
8
+ export function readLocalStorage<T>(key: string, initialValue: T): T {
9
9
  try {
10
10
  const item = globalThis.localStorage.getItem(key)
11
11
  return item === null ? initialValue : (JSON.parse(item) as T)
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.8.2'
1
+ export const version = '2.10.0'
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
3
- declare const NCBIBlastAutomaticPanel: ({ handleClose, feature, model, children, baseUrl, }: {
4
- model: AbstractTrackModel;
5
- feature: Feature;
6
- baseUrl: string;
7
- handleClose: () => void;
8
- children: React.ReactNode;
9
- }) => React.JSX.Element;
10
- export default NCBIBlastAutomaticPanel;
@@ -1,74 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Typography } from '@mui/material';
3
- import { observer } from 'mobx-react';
4
- import { makeStyles } from 'tss-react/mui';
5
- import MsaAlgorithmSelect from './MsaAlgorithmSelect';
6
- import { blastLaunchView } from './blastLaunchView';
7
- import ExternalLink from '../../../components/ExternalLink';
8
- import TextField2 from '../../../components/TextField2';
9
- import { getBlastViewTitle, getLinearGenomeView } from '../../util';
10
- import LaunchPanelContent from '../LaunchPanelContent';
11
- import SubmitCancelActions from '../SubmitCancelActions';
12
- import TranscriptSelector from '../TranscriptSelector';
13
- import { useTranscriptSelection } from '../useTranscriptSelection';
14
- const useStyles = makeStyles()({
15
- marginBottom: {
16
- marginBottom: 16,
17
- },
18
- ridField: {
19
- width: 150,
20
- },
21
- infoText: {
22
- marginTop: 20,
23
- },
24
- });
25
- const NCBIBlastRIDPanel = observer(function ({ handleClose, feature, model, children, baseUrl, }) {
26
- const { classes } = useStyles();
27
- const view = getLinearGenomeView(model);
28
- const [launchViewError, setLaunchViewError] = useState();
29
- const [rid, setRid] = useState('');
30
- const [selectedMsaAlgorithm, setSelectedMsaAlgorithm] = useState('clustalo');
31
- const transcriptSelection = useTranscriptSelection({ feature, view });
32
- const { selectedTranscript, proteinSequence } = transcriptSelection;
33
- const e = transcriptSelection.error ?? launchViewError;
34
- const trimmedRid = rid.trim();
35
- return (React.createElement(React.Fragment, null,
36
- React.createElement(LaunchPanelContent, { error: e },
37
- children,
38
- React.createElement(Typography, { variant: "body2", className: classes.marginBottom }, "Enter the RID (Request ID) from a previously submitted NCBI BLAST query. You can find the RID in the BLAST results page URL or at the top of the results page. RIDs are typically valid for 24-36 hours after submission."),
39
- React.createElement(TextField2, { variant: "outlined", label: "BLAST RID", placeholder: "e.g., ABC12345", fullWidth: true, className: classes.marginBottom, value: rid, onChange: event => {
40
- setRid(event.target.value);
41
- } }),
42
- trimmedRid ? (React.createElement(Typography, { variant: "body2", className: classes.marginBottom },
43
- React.createElement(ExternalLink, { href: `${baseUrl}?CMD=Get&RID=${trimmedRid}` }, "View BLAST results on NCBI"))) : null,
44
- React.createElement(MsaAlgorithmSelect, { className: classes.ridField, value: selectedMsaAlgorithm, onChange: setSelectedMsaAlgorithm }),
45
- React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
46
- React.createElement(Typography, { className: classes.infoText }, "This will fetch the BLAST results for the provided RID and run them through a multiple sequence alignment. The protein sequence from the selected transcript will be added as the query sequence in the MSA.")),
47
- React.createElement(SubmitCancelActions, { submitDisabled: !proteinSequence || !trimmedRid, onSubmit: () => {
48
- try {
49
- if (selectedTranscript && trimmedRid) {
50
- setLaunchViewError(undefined);
51
- blastLaunchView({
52
- feature: selectedTranscript,
53
- view,
54
- newViewTitle: getBlastViewTitle(feature, selectedTranscript),
55
- blastParams: {
56
- baseUrl,
57
- blastProgram: 'blastp',
58
- blastDatabase: 'nr',
59
- msaAlgorithm: selectedMsaAlgorithm,
60
- selectedTranscript,
61
- proteinSequence,
62
- rid: trimmedRid,
63
- },
64
- });
65
- handleClose();
66
- }
67
- }
68
- catch (e) {
69
- console.error(e);
70
- setLaunchViewError(e);
71
- }
72
- }, onCancel: handleClose })));
73
- });
74
- export default NCBIBlastRIDPanel;
@@ -1,5 +0,0 @@
1
- import React from 'react';
2
- export default function NCBISettingsDialog({ handleClose, baseUrl, }: {
3
- handleClose: (arg?: string) => void;
4
- baseUrl: string;
5
- }): React.JSX.Element;
@@ -1,7 +0,0 @@
1
- export declare const BASE_BLAST_URL = "https://blast.ncbi.nlm.nih.gov/Blast.cgi";
2
- export declare const msaAlgorithms: readonly ["clustalo", "muscle", "kalign", "mafft"];
3
- export type MsaAlgorithm = (typeof msaAlgorithms)[number];
4
- export declare const blastDatabaseOptions: readonly ["nr", "nr_cluster_seq"];
5
- export type BlastDatabase = (typeof blastDatabaseOptions)[number];
6
- export declare const blastPrograms: readonly ["blastp", "quick-blastp"];
7
- export type BlastProgram = (typeof blastPrograms)[number];
@@ -1,4 +0,0 @@
1
- export const BASE_BLAST_URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi';
2
- export const msaAlgorithms = ['clustalo', 'muscle', 'kalign', 'mafft'];
3
- export const blastDatabaseOptions = ['nr', 'nr_cluster_seq'];
4
- export const blastPrograms = ['blastp', 'quick-blastp'];
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- declare function RIDLink({ baseUrl, rid }: {
3
- rid: string;
4
- baseUrl: string;
5
- }): React.JSX.Element;
6
- export default RIDLink;
@@ -1,12 +0,0 @@
1
- import React from 'react';
2
- import { Typography } from '@mui/material';
3
- import ExternalLink from '../../components/ExternalLink';
4
- function RIDLink({ baseUrl, rid }) {
5
- return (React.createElement(Typography, null,
6
- "RID ",
7
- rid,
8
- " (",
9
- React.createElement(ExternalLink, { href: `${baseUrl}?CMD=Get&RID=${rid}` }, "see status"),
10
- ")"));
11
- }
12
- export default RIDLink;
@@ -1,30 +0,0 @@
1
- import type { BlastDatabase, BlastProgram } from '../LaunchMsaView/components/NCBIBlastQuery/consts';
2
- export declare function queryBlastFromRid({ rid, baseUrl, onProgress, }: {
3
- rid: string;
4
- baseUrl: string;
5
- onProgress: (arg: string) => void;
6
- }): Promise<{
7
- rid: string;
8
- hits: {
9
- description: import("./types").BlastHitDescription[];
10
- hsps: {
11
- hseq: string;
12
- }[];
13
- }[];
14
- }>;
15
- export declare function queryBlast({ query, blastDatabase, blastProgram, baseUrl, onProgress, onRid, }: {
16
- query: string;
17
- blastDatabase: BlastDatabase;
18
- blastProgram: BlastProgram;
19
- baseUrl: string;
20
- onProgress: (arg: string) => void;
21
- onRid: (arg: string) => void;
22
- }): Promise<{
23
- rid: string;
24
- hits: {
25
- description: import("./types").BlastHitDescription[];
26
- hsps: {
27
- hseq: string;
28
- }[];
29
- }[];
30
- }>;
@@ -1,84 +0,0 @@
1
- import { jsonfetch, textfetch } from './fetch';
2
- import { pollLoop } from './poll';
3
- export async function queryBlastFromRid({ rid, baseUrl, onProgress, }) {
4
- onProgress(`Checking BLAST status for RID: ${rid}...`);
5
- await waitForRid({
6
- rid,
7
- onProgress,
8
- baseUrl,
9
- });
10
- const ret = await jsonfetch(`${baseUrl}?CMD=Get&RID=${rid}&FORMAT_TYPE=JSON2_S&FORMAT_OBJECT=Alignment`);
11
- return {
12
- rid,
13
- hits: ret.BlastOutput2[0]?.report.results.search.hits ?? [],
14
- };
15
- }
16
- export async function queryBlast({ query, blastDatabase, blastProgram, baseUrl, onProgress, onRid, }) {
17
- onProgress('Submitting to NCBI BLAST...');
18
- const { rid } = await initialQuery({
19
- query,
20
- blastDatabase,
21
- blastProgram,
22
- baseUrl,
23
- });
24
- onRid(rid);
25
- return queryBlastFromRid({ rid, baseUrl, onProgress });
26
- }
27
- async function initialQuery({ query, blastProgram, blastDatabase, baseUrl, }) {
28
- const res = await textfetch(baseUrl, {
29
- method: 'POST',
30
- body: new URLSearchParams({
31
- CMD: 'Put',
32
- PROGRAM: blastProgram === 'quick-blastp' ? 'blastp' : blastProgram,
33
- DATABASE: blastDatabase,
34
- QUERY: query,
35
- ...(blastDatabase === 'nr_cluster_seq'
36
- ? {
37
- CLUSTERED_DB: 'on',
38
- DB_TYPE: 'Experimental Databases',
39
- }
40
- : {}),
41
- ...(blastProgram === 'quick-blastp'
42
- ? { BLAST_PROGRAMS: 'kmerBlastp' }
43
- : {}),
44
- }),
45
- });
46
- // the initial submission/query to the BLAST "REST API" does not return JSON
47
- // as a response (e.g. FORMAT_TYPE=JSON does not work), so the RID is
48
- // literally parsed from the text of the HTML that is returned
49
- const rid = /^ {4}RID = (.*$)/m.exec(res)?.[1];
50
- const rtoe = /^ {4}RTOE = (.*$)/m.exec(res)?.[1];
51
- if (!rid) {
52
- throw new Error('Failed to get RID from BLAST request');
53
- }
54
- return {
55
- rid,
56
- rtoe,
57
- };
58
- }
59
- async function waitForRid({ rid, onProgress, baseUrl, }) {
60
- await pollLoop({
61
- intervalSeconds: 20,
62
- onCountdown: s => {
63
- onProgress(`Re-checking BLAST status in... ${s}`);
64
- },
65
- check: async () => {
66
- const res = await textfetch(`${baseUrl}?CMD=Get&FORMAT_OBJECT=SearchInfo&RID=${rid}`);
67
- const status = /\s+Status=(\S+)/m.exec(res)?.[1];
68
- const hasHits = /\s+ThereAreHits=yes/m.test(res);
69
- if (status === 'WAITING') {
70
- return false;
71
- }
72
- if (status === 'FAILED') {
73
- throw new Error(`BLAST ${rid} failed`);
74
- }
75
- if (status === 'READY') {
76
- if (hasHits) {
77
- return true;
78
- }
79
- throw new Error('No hits found');
80
- }
81
- throw new Error(`BLAST ${rid} returned unexpected status: ${status ?? 'unknown'}`);
82
- },
83
- });
84
- }
@@ -1,137 +0,0 @@
1
- import React, { useState } from 'react'
2
-
3
- import { Typography } from '@mui/material'
4
- import { observer } from 'mobx-react'
5
- import { makeStyles } from 'tss-react/mui'
6
-
7
- import MsaAlgorithmSelect from './MsaAlgorithmSelect'
8
- import { blastLaunchView } from './blastLaunchView'
9
- import ExternalLink from '../../../components/ExternalLink'
10
- import TextField2 from '../../../components/TextField2'
11
- import { getBlastViewTitle, getLinearGenomeView } from '../../util'
12
- import LaunchPanelContent from '../LaunchPanelContent'
13
- import SubmitCancelActions from '../SubmitCancelActions'
14
- import TranscriptSelector from '../TranscriptSelector'
15
- import { useTranscriptSelection } from '../useTranscriptSelection'
16
-
17
- import type { MsaAlgorithm } from './consts'
18
- import type { AbstractTrackModel, Feature } from '@jbrowse/core/util'
19
-
20
- const useStyles = makeStyles()({
21
- marginBottom: {
22
- marginBottom: 16,
23
- },
24
- ridField: {
25
- width: 150,
26
- },
27
- infoText: {
28
- marginTop: 20,
29
- },
30
- })
31
-
32
- const NCBIBlastRIDPanel = observer(function ({
33
- handleClose,
34
- feature,
35
- model,
36
- children,
37
- baseUrl,
38
- }: {
39
- model: AbstractTrackModel
40
- feature: Feature
41
- baseUrl: string
42
- handleClose: () => void
43
- children: React.ReactNode
44
- }) {
45
- const { classes } = useStyles()
46
- const view = getLinearGenomeView(model)
47
- const [launchViewError, setLaunchViewError] = useState<unknown>()
48
- const [rid, setRid] = useState('')
49
- const [selectedMsaAlgorithm, setSelectedMsaAlgorithm] =
50
- useState<MsaAlgorithm>('clustalo')
51
-
52
- const transcriptSelection = useTranscriptSelection({ feature, view })
53
- const { selectedTranscript, proteinSequence } = transcriptSelection
54
-
55
- const e = transcriptSelection.error ?? launchViewError
56
- const trimmedRid = rid.trim()
57
-
58
- return (
59
- <>
60
- <LaunchPanelContent error={e}>
61
- {children}
62
-
63
- <Typography variant="body2" className={classes.marginBottom}>
64
- Enter the RID (Request ID) from a previously submitted NCBI BLAST
65
- query. You can find the RID in the BLAST results page URL or at the
66
- top of the results page. RIDs are typically valid for 24-36 hours
67
- after submission.
68
- </Typography>
69
-
70
- <TextField2
71
- variant="outlined"
72
- label="BLAST RID"
73
- placeholder="e.g., ABC12345"
74
- fullWidth
75
- className={classes.marginBottom}
76
- value={rid}
77
- onChange={event => {
78
- setRid(event.target.value)
79
- }}
80
- />
81
-
82
- {trimmedRid ? (
83
- <Typography variant="body2" className={classes.marginBottom}>
84
- <ExternalLink href={`${baseUrl}?CMD=Get&RID=${trimmedRid}`}>
85
- View BLAST results on NCBI
86
- </ExternalLink>
87
- </Typography>
88
- ) : null}
89
-
90
- <MsaAlgorithmSelect
91
- className={classes.ridField}
92
- value={selectedMsaAlgorithm}
93
- onChange={setSelectedMsaAlgorithm}
94
- />
95
-
96
- <TranscriptSelector feature={feature} {...transcriptSelection} />
97
-
98
- <Typography className={classes.infoText}>
99
- This will fetch the BLAST results for the provided RID and run them
100
- through a multiple sequence alignment. The protein sequence from the
101
- selected transcript will be added as the query sequence in the MSA.
102
- </Typography>
103
- </LaunchPanelContent>
104
- <SubmitCancelActions
105
- submitDisabled={!proteinSequence || !trimmedRid}
106
- onSubmit={() => {
107
- try {
108
- if (selectedTranscript && trimmedRid) {
109
- setLaunchViewError(undefined)
110
- blastLaunchView({
111
- feature: selectedTranscript,
112
- view,
113
- newViewTitle: getBlastViewTitle(feature, selectedTranscript),
114
- blastParams: {
115
- baseUrl,
116
- blastProgram: 'blastp',
117
- blastDatabase: 'nr',
118
- msaAlgorithm: selectedMsaAlgorithm,
119
- selectedTranscript,
120
- proteinSequence,
121
- rid: trimmedRid,
122
- },
123
- })
124
- handleClose()
125
- }
126
- } catch (e) {
127
- console.error(e)
128
- setLaunchViewError(e)
129
- }
130
- }}
131
- onCancel={handleClose}
132
- />
133
- </>
134
- )
135
- })
136
-
137
- export default NCBIBlastRIDPanel
@@ -1,10 +0,0 @@
1
- export const BASE_BLAST_URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi'
2
-
3
- export const msaAlgorithms = ['clustalo', 'muscle', 'kalign', 'mafft'] as const
4
- export type MsaAlgorithm = (typeof msaAlgorithms)[number]
5
-
6
- export const blastDatabaseOptions = ['nr', 'nr_cluster_seq'] as const
7
- export type BlastDatabase = (typeof blastDatabaseOptions)[number]
8
-
9
- export const blastPrograms = ['blastp', 'quick-blastp'] as const
10
- export type BlastProgram = (typeof blastPrograms)[number]
@@ -1,19 +0,0 @@
1
- import React from 'react'
2
-
3
- import { Typography } from '@mui/material'
4
-
5
- import ExternalLink from '../../components/ExternalLink'
6
-
7
- function RIDLink({ baseUrl, rid }: { rid: string; baseUrl: string }) {
8
- return (
9
- <Typography>
10
- RID {rid} (
11
- <ExternalLink href={`${baseUrl}?CMD=Get&RID=${rid}`}>
12
- see status
13
- </ExternalLink>
14
- )
15
- </Typography>
16
- )
17
- }
18
-
19
- export default RIDLink
@@ -1,143 +0,0 @@
1
- import { jsonfetch, textfetch } from './fetch'
2
- import { pollLoop } from './poll'
3
-
4
- import type { BlastResults } from './types'
5
- import type {
6
- BlastDatabase,
7
- BlastProgram,
8
- } from '../LaunchMsaView/components/NCBIBlastQuery/consts'
9
-
10
- export async function queryBlastFromRid({
11
- rid,
12
- baseUrl,
13
- onProgress,
14
- }: {
15
- rid: string
16
- baseUrl: string
17
- onProgress: (arg: string) => void
18
- }) {
19
- onProgress(`Checking BLAST status for RID: ${rid}...`)
20
- await waitForRid({
21
- rid,
22
- onProgress,
23
- baseUrl,
24
- })
25
- const ret = await jsonfetch<BlastResults>(
26
- `${baseUrl}?CMD=Get&RID=${rid}&FORMAT_TYPE=JSON2_S&FORMAT_OBJECT=Alignment`,
27
- )
28
- return {
29
- rid,
30
- hits: ret.BlastOutput2[0]?.report.results.search.hits ?? [],
31
- }
32
- }
33
-
34
- export async function queryBlast({
35
- query,
36
- blastDatabase,
37
- blastProgram,
38
- baseUrl,
39
- onProgress,
40
- onRid,
41
- }: {
42
- query: string
43
- blastDatabase: BlastDatabase
44
- blastProgram: BlastProgram
45
- baseUrl: string
46
- onProgress: (arg: string) => void
47
- onRid: (arg: string) => void
48
- }) {
49
- onProgress('Submitting to NCBI BLAST...')
50
- const { rid } = await initialQuery({
51
- query,
52
- blastDatabase,
53
- blastProgram,
54
- baseUrl,
55
- })
56
- onRid(rid)
57
- return queryBlastFromRid({ rid, baseUrl, onProgress })
58
- }
59
-
60
- async function initialQuery({
61
- query,
62
- blastProgram,
63
- blastDatabase,
64
- baseUrl,
65
- }: {
66
- query: string
67
- blastProgram: BlastProgram
68
- blastDatabase: BlastDatabase
69
- baseUrl: string
70
- }) {
71
- const res = await textfetch(baseUrl, {
72
- method: 'POST',
73
- body: new URLSearchParams({
74
- CMD: 'Put',
75
- PROGRAM: blastProgram === 'quick-blastp' ? 'blastp' : blastProgram,
76
- DATABASE: blastDatabase,
77
- QUERY: query,
78
- ...(blastDatabase === 'nr_cluster_seq'
79
- ? {
80
- CLUSTERED_DB: 'on',
81
- DB_TYPE: 'Experimental Databases',
82
- }
83
- : {}),
84
- ...(blastProgram === 'quick-blastp'
85
- ? { BLAST_PROGRAMS: 'kmerBlastp' }
86
- : {}),
87
- }),
88
- })
89
-
90
- // the initial submission/query to the BLAST "REST API" does not return JSON
91
- // as a response (e.g. FORMAT_TYPE=JSON does not work), so the RID is
92
- // literally parsed from the text of the HTML that is returned
93
- const rid = /^ {4}RID = (.*$)/m.exec(res)?.[1]
94
- const rtoe = /^ {4}RTOE = (.*$)/m.exec(res)?.[1]
95
-
96
- if (!rid) {
97
- throw new Error('Failed to get RID from BLAST request')
98
- }
99
- return {
100
- rid,
101
- rtoe,
102
- }
103
- }
104
-
105
- async function waitForRid({
106
- rid,
107
- onProgress,
108
- baseUrl,
109
- }: {
110
- rid: string
111
- onProgress: (arg: string) => void
112
- baseUrl: string
113
- }) {
114
- await pollLoop({
115
- intervalSeconds: 20,
116
- onCountdown: s => {
117
- onProgress(`Re-checking BLAST status in... ${s}`)
118
- },
119
- check: async () => {
120
- const res = await textfetch(
121
- `${baseUrl}?CMD=Get&FORMAT_OBJECT=SearchInfo&RID=${rid}`,
122
- )
123
- const status = /\s+Status=(\S+)/m.exec(res)?.[1]
124
- const hasHits = /\s+ThereAreHits=yes/m.test(res)
125
-
126
- if (status === 'WAITING') {
127
- return false
128
- }
129
- if (status === 'FAILED') {
130
- throw new Error(`BLAST ${rid} failed`)
131
- }
132
- if (status === 'READY') {
133
- if (hasHits) {
134
- return true
135
- }
136
- throw new Error('No hits found')
137
- }
138
- throw new Error(
139
- `BLAST ${rid} returned unexpected status: ${status ?? 'unknown'}`,
140
- )
141
- },
142
- })
143
- }