react-msaview 3.1.11 → 3.1.12

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 (55) hide show
  1. package/bundle/index.js +31 -31
  2. package/dist/components/SequenceTextArea.js +4 -0
  3. package/dist/components/SequenceTextArea.js.map +1 -1
  4. package/dist/components/dialogs/DomainDialog.d.ts +6 -0
  5. package/dist/components/dialogs/DomainDialog.js +19 -0
  6. package/dist/components/dialogs/DomainDialog.js.map +1 -0
  7. package/dist/components/dialogs/InterProScanPanel.d.ts +7 -0
  8. package/dist/components/dialogs/{InterProScanDialog.js → InterProScanPanel.js} +36 -8
  9. package/dist/components/dialogs/InterProScanPanel.js.map +1 -0
  10. package/dist/components/dialogs/TabPanel.d.ts +6 -0
  11. package/dist/components/dialogs/TabPanel.js +6 -0
  12. package/dist/components/dialogs/TabPanel.js.map +1 -0
  13. package/dist/components/dialogs/{InterProScanDialog.d.ts → UserProvidedResultPanel.d.ts} +2 -2
  14. package/dist/components/dialogs/UserProvidedResultPanel.js +56 -0
  15. package/dist/components/dialogs/UserProvidedResultPanel.js.map +1 -0
  16. package/dist/components/header/Header.js +0 -2
  17. package/dist/components/header/Header.js.map +1 -1
  18. package/dist/components/header/HeaderMenuExtra.js +36 -25
  19. package/dist/components/header/HeaderMenuExtra.js.map +1 -1
  20. package/dist/components/header/HeaderStatusArea.d.ts +1 -1
  21. package/dist/components/header/HeaderStatusArea.js +3 -2
  22. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  23. package/dist/components/msa/MSACanvasBlock.js +1 -1
  24. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +2 -2
  25. package/dist/components/msa/renderMSABlock.js +4 -4
  26. package/dist/fetchUtils.d.ts +1 -1
  27. package/dist/fetchUtils.js.map +1 -1
  28. package/dist/launchInterProScan.d.ts +9 -3
  29. package/dist/launchInterProScan.js +58 -21
  30. package/dist/launchInterProScan.js.map +1 -1
  31. package/dist/model.d.ts +11 -33
  32. package/dist/model.js +4 -65
  33. package/dist/model.js.map +1 -1
  34. package/dist/renderToSvg.js +0 -23
  35. package/dist/renderToSvg.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +1 -1
  39. package/src/components/SequenceTextArea.tsx +8 -0
  40. package/src/components/dialogs/DomainDialog.tsx +38 -0
  41. package/src/components/dialogs/{InterProScanDialog.tsx → InterProScanPanel.tsx} +41 -10
  42. package/src/components/dialogs/TabPanel.tsx +19 -0
  43. package/src/components/dialogs/UserProvidedResultPanel.tsx +119 -0
  44. package/src/components/header/Header.tsx +0 -2
  45. package/src/components/header/HeaderMenuExtra.tsx +41 -28
  46. package/src/components/header/HeaderStatusArea.tsx +7 -2
  47. package/src/components/msa/MSACanvasBlock.tsx +1 -1
  48. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +2 -2
  49. package/src/components/msa/renderMSABlock.ts +4 -4
  50. package/src/fetchUtils.ts +2 -2
  51. package/src/launchInterProScan.ts +70 -23
  52. package/src/model.ts +5 -86
  53. package/src/renderToSvg.tsx +0 -24
  54. package/src/version.ts +1 -1
  55. package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
@@ -1,16 +1,17 @@
1
1
  import React, { useState } from 'react'
2
2
  import { observer } from 'mobx-react'
3
- import { Dialog } from '@jbrowse/core/ui'
4
3
  import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
5
4
 
6
5
  // locals
7
6
  import { MsaViewModel } from '../../model'
7
+ import { getSession } from '@jbrowse/core/util'
8
+ import { launchInterProScan } from '../../launchInterProScan'
8
9
 
9
- const FeatureTypeDialog = observer(function ({
10
- onClose,
10
+ const InterProScanDialog = observer(function ({
11
+ handleClose,
11
12
  model,
12
13
  }: {
13
- onClose: () => void
14
+ handleClose: () => void
14
15
  model: MsaViewModel
15
16
  }) {
16
17
  const [vals, setVals] = useState([
@@ -146,7 +147,7 @@ const FeatureTypeDialog = observer(function ({
146
147
  const [show, setShow] = useState(false)
147
148
 
148
149
  return (
149
- <Dialog onClose={() => onClose()} open title="Feature types" maxWidth="xl">
150
+ <>
150
151
  <DialogContent>
151
152
  <Typography>
152
153
  This will run InterProScan on all rows of the current MSA
@@ -209,22 +210,52 @@ const FeatureTypeDialog = observer(function ({
209
210
  ) : null}
210
211
  </DialogContent>
211
212
  <DialogActions>
212
- <Button variant="contained" color="secondary" onClick={() => onClose()}>
213
+ <Button
214
+ variant="contained"
215
+ color="secondary"
216
+ onClick={() => handleClose()}
217
+ >
213
218
  Cancel
214
219
  </Button>
215
220
  <Button
216
221
  variant="contained"
217
222
  color="primary"
218
223
  onClick={() => {
219
- model.queryInterProScan(programs).catch(e => model.setError(e))
220
- onClose()
224
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
225
+ ;(async () => {
226
+ try {
227
+ const { rows } = model
228
+ if (rows.length > 140) {
229
+ throw new Error(
230
+ 'Too many sequences, please run InterProScan offline',
231
+ )
232
+ }
233
+ await launchInterProScan({
234
+ algorithm: 'interproscan',
235
+ programs: programs,
236
+ seq: rows
237
+ .map(row => [row[0], row[1].replaceAll('-', '')])
238
+ .filter(f => !!f[1])
239
+ .map(row => `>${row[0]}\n${row[1]}`)
240
+ .join('\n'),
241
+ onProgress: arg => model.setStatus(arg),
242
+ model,
243
+ })
244
+ } catch (e) {
245
+ console.error(e)
246
+ getSession(model).notifyError(`${e}`, e)
247
+ } finally {
248
+ model.setStatus()
249
+ }
250
+ })()
251
+ handleClose()
221
252
  }}
222
253
  >
223
254
  Send sequences to InterProScan
224
255
  </Button>
225
256
  </DialogActions>
226
- </Dialog>
257
+ </>
227
258
  )
228
259
  })
229
260
 
230
- export default FeatureTypeDialog
261
+ export default InterProScanDialog
@@ -0,0 +1,19 @@
1
+ import React from 'react'
2
+
3
+ // this is from MUI example
4
+ export default function TabPanel({
5
+ children,
6
+ value,
7
+ index,
8
+ ...other
9
+ }: {
10
+ children?: React.ReactNode
11
+ index: number
12
+ value: number
13
+ }) {
14
+ return (
15
+ <div role="tabpanel" hidden={value !== index} {...other}>
16
+ {value === index && <div>{children}</div>}
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,119 @@
1
+ import React, { useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import {
4
+ Button,
5
+ DialogActions,
6
+ DialogContent,
7
+ FormControlLabel,
8
+ FormControl,
9
+ Radio,
10
+ RadioGroup,
11
+ TextField,
12
+ Typography,
13
+ } from '@mui/material'
14
+ import { getSession } from '@jbrowse/core/util'
15
+
16
+ // locals
17
+ import { MsaViewModel } from '../../model'
18
+ import { jsonfetch } from '../../fetchUtils'
19
+ import { InterProScanResponse } from '../../launchInterProScan'
20
+
21
+ const FeatureTypeDialog = observer(function ({
22
+ handleClose,
23
+ model,
24
+ }: {
25
+ handleClose: () => void
26
+ model: MsaViewModel
27
+ }) {
28
+ const [file, setFile] = useState<File>()
29
+ const [choice, setChoice] = useState('file')
30
+ const [interProURL, setInterProURL] = useState('')
31
+
32
+ return (
33
+ <>
34
+ <DialogContent>
35
+ <div style={{ display: 'flex', margin: 30 }}>
36
+ <Typography>
37
+ Open a JSON file of InterProScan results that you run remotely on
38
+ EBI servers or locally
39
+ </Typography>
40
+
41
+ <FormControl component="fieldset">
42
+ <RadioGroup
43
+ value={choice}
44
+ onChange={event => setChoice(event.target.value)}
45
+ >
46
+ <FormControlLabel value="url" control={<Radio />} label="URL" />
47
+ <FormControlLabel value="file" control={<Radio />} label="File" />
48
+ </RadioGroup>
49
+ </FormControl>
50
+ {choice === 'url' ? (
51
+ <div>
52
+ <Typography>Open a InterProScan JSON file remote URL</Typography>
53
+ <TextField
54
+ label="URL"
55
+ value={interProURL}
56
+ onChange={event => setInterProURL(event.target.value)}
57
+ />
58
+ </div>
59
+ ) : null}
60
+ {choice === 'file' ? (
61
+ <div style={{ paddingTop: 20 }}>
62
+ <Typography>
63
+ Open a InterProScan JSON file file from your local drive
64
+ </Typography>
65
+ <Button variant="outlined" component="label">
66
+ Choose File
67
+ <input
68
+ type="file"
69
+ hidden
70
+ onChange={({ target }) => {
71
+ const file = target?.files?.[0]
72
+ if (file) {
73
+ setFile(file)
74
+ }
75
+ }}
76
+ />
77
+ </Button>
78
+ </div>
79
+ ) : null}
80
+ </div>
81
+ </DialogContent>
82
+ <DialogActions>
83
+ <Button
84
+ variant="contained"
85
+ color="primary"
86
+ onClick={() => {
87
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
88
+ ;(async () => {
89
+ try {
90
+ const ret: InterProScanResponse = file
91
+ ? JSON.parse(await file.text())
92
+ : await jsonfetch(interProURL)
93
+
94
+ model.setLoadedInterProAnnotations(
95
+ Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
96
+ )
97
+ model.setShowDomains(true)
98
+ getSession(model).notify(
99
+ 'Loaded interproscan results',
100
+ 'success',
101
+ )
102
+ } catch (e) {
103
+ console.error(e)
104
+ getSession(model).notifyError(`${e}`, e)
105
+ } finally {
106
+ model.setStatus()
107
+ }
108
+ })()
109
+ handleClose()
110
+ }}
111
+ >
112
+ Open results
113
+ </Button>
114
+ </DialogActions>
115
+ </>
116
+ )
117
+ })
118
+
119
+ export default FeatureTypeDialog
@@ -13,7 +13,6 @@ import ZoomControls from './ZoomControls'
13
13
  import MultiAlignmentSelector from './MultiAlignmentSelector'
14
14
  import HeaderInfoArea from './HeaderInfoArea'
15
15
  import HeaderStatusArea from './HeaderStatusArea'
16
- import HeaderMenu from './HeaderMenu'
17
16
  import HeaderMenuExtra from './HeaderMenuExtra'
18
17
 
19
18
  const AboutDialog = lazy(() => import('../dialogs/AboutDialog'))
@@ -21,7 +20,6 @@ const AboutDialog = lazy(() => import('../dialogs/AboutDialog'))
21
20
  const Header = observer(function ({ model }: { model: MsaViewModel }) {
22
21
  return (
23
22
  <div style={{ display: 'flex' }}>
24
- <HeaderMenu model={model} />
25
23
  <ZoomControls model={model} />
26
24
  <HeaderMenuExtra model={model} />
27
25
  <MultiAlignmentSelector model={model} />
@@ -3,7 +3,6 @@ import { observer } from 'mobx-react'
3
3
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
4
 
5
5
  // locals
6
- import { MsaViewModel } from '../../model'
7
6
 
8
7
  // icons
9
8
  import MoreVert from '@mui/icons-material/MoreVert'
@@ -13,18 +12,50 @@ import FilterAlt from '@mui/icons-material/FilterAlt'
13
12
  import Search from '@mui/icons-material/Search'
14
13
  import PhotoCamera from '@mui/icons-material/PhotoCamera'
15
14
  import RestartAlt from '@mui/icons-material/RestartAlt'
15
+ import FolderOpen from '@mui/icons-material/FolderOpen'
16
+ import Settings from '@mui/icons-material/Settings'
17
+ import Assignment from '@mui/icons-material/Assignment'
18
+ import List from '@mui/icons-material/List'
19
+
20
+ // locals
21
+ import { MsaViewModel } from '../../model'
16
22
 
17
23
  // lazies
24
+ const SettingsDialog = lazy(() => import('../dialogs/SettingsDialog'))
25
+ const MetadataDialog = lazy(() => import('../dialogs/MetadataDialog'))
26
+ const TracklistDialog = lazy(() => import('../dialogs/TracklistDialog'))
18
27
  const ExportSVGDialog = lazy(() => import('../dialogs/ExportSVGDialog'))
19
28
  const FeatureFilterDialog = lazy(() => import('../dialogs/FeatureDialog'))
20
- const InterProScanDialog = lazy(() => import('../dialogs/InterProScanDialog'))
29
+ const DomainDialog = lazy(() => import('../dialogs/DomainDialog'))
21
30
 
22
31
  const HeaderMenuExtra = observer(function ({ model }: { model: MsaViewModel }) {
23
- const { featureMode, subFeatureRows, noAnnotations, interProScanJobIds } =
24
- model
32
+ const { showDomains, subFeatureRows, noAnnotations } = model
25
33
  return (
26
34
  <CascadingMenuButton
27
35
  menuItems={[
36
+ {
37
+ label: 'Return to import form',
38
+ icon: FolderOpen,
39
+ onClick: () => model.reset(),
40
+ },
41
+ {
42
+ label: 'Settings',
43
+ onClick: () =>
44
+ model.queueDialog(onClose => [SettingsDialog, { model, onClose }]),
45
+ icon: Settings,
46
+ },
47
+ {
48
+ label: 'Metadata',
49
+ onClick: () =>
50
+ model.queueDialog(onClose => [MetadataDialog, { model, onClose }]),
51
+ icon: Assignment,
52
+ },
53
+ {
54
+ label: 'Extra tracks',
55
+ onClick: () =>
56
+ model.queueDialog(onClose => [TracklistDialog, { model, onClose }]),
57
+ icon: List,
58
+ },
28
59
  {
29
60
  label: 'Reset zoom to default',
30
61
  icon: RestartAlt,
@@ -47,9 +78,9 @@ const HeaderMenuExtra = observer(function ({ model }: { model: MsaViewModel }) {
47
78
  label:
48
79
  'Show domains' + (noAnnotations ? ' (no domains loaded)' : ''),
49
80
  icon: Visibility,
50
- checked: featureMode,
81
+ checked: showDomains,
51
82
  type: 'checkbox',
52
- onClick: () => model.setFeatureMode(!featureMode),
83
+ onClick: () => model.setShowDomains(!showDomains),
53
84
  },
54
85
  {
55
86
  label: 'Use sub-row layout',
@@ -69,32 +100,14 @@ const HeaderMenuExtra = observer(function ({ model }: { model: MsaViewModel }) {
69
100
  },
70
101
  },
71
102
  {
72
- label: 'Query InterProScan for domains...',
103
+ label: 'View domains',
73
104
  icon: Search,
74
105
  onClick: () =>
75
- model.queueDialog(onClose => [
76
- InterProScanDialog,
77
- { onClose, model },
106
+ model.queueDialog(handleClose => [
107
+ DomainDialog,
108
+ { handleClose, model },
78
109
  ]),
79
110
  },
80
- {
81
- label: 'Load previous InterProScan results...',
82
- icon: Search,
83
- type: 'subMenu',
84
- subMenu: interProScanJobIds.length
85
- ? interProScanJobIds.map(({ jobId, date }) => ({
86
- label:
87
- new Date(date).toLocaleString('en-US') + ' - ' + jobId,
88
- onClick: () => model.loadInterProScanResults(jobId),
89
- }))
90
- : [
91
- {
92
- label: 'No previous searches',
93
- disabled: true,
94
- onClick: () => {},
95
- },
96
- ],
97
- },
98
111
  ],
99
112
  },
100
113
  ...(model.extraViewMenuItems?.() || []),
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import { Typography } from '@mui/material'
3
3
  import { observer } from 'mobx-react'
4
4
  import { makeStyles } from 'tss-react/mui'
5
+ import { LoadingEllipses } from '@jbrowse/core/ui'
5
6
 
6
7
  // locals
7
8
  import { MsaViewModel } from '../../model'
@@ -13,12 +14,16 @@ const useStyles = makeStyles()({
13
14
  },
14
15
  })
15
16
 
16
- const HeaderStatusArea = observer(({ model }: { model: MsaViewModel }) => {
17
+ const HeaderStatusArea = observer(function ({
18
+ model,
19
+ }: {
20
+ model: MsaViewModel
21
+ }) {
17
22
  const { status } = model
18
23
  const { classes } = useStyles()
19
24
  return status ? (
20
25
  <Typography className={classes.margin}>
21
- {status.msg}{' '}
26
+ <LoadingEllipses message={status.msg} component="span" />{' '}
22
27
  {status.url ? (
23
28
  <a href={status.url} target="_blank" rel="noreferrer">
24
29
  (status)
@@ -45,7 +45,7 @@ const MSABlock = observer(function ({
45
45
  return autorun(() => {
46
46
  ctx.resetTransform()
47
47
  ctx.clearRect(0, 0, blockSize, blockSize)
48
- if (model.featureMode) {
48
+ if (model.showDomains) {
49
49
  renderBoxFeatureCanvasBlock({
50
50
  ctx,
51
51
  offsetX,
@@ -19,9 +19,9 @@ export function renderBoxFeatureCanvasBlock({
19
19
  highResScaleFactorOverride?: number
20
20
  blockSizeYOverride?: number
21
21
  }) {
22
- const { hierarchy, blockSize, rowHeight, highResScaleFactor, featureMode } =
22
+ const { hierarchy, blockSize, rowHeight, highResScaleFactor, showDomains } =
23
23
  model
24
- if (featureMode) {
24
+ if (showDomains) {
25
25
  const k = highResScaleFactorOverride || highResScaleFactor
26
26
  const by = blockSizeYOverride || blockSize
27
27
  ctx.resetTransform()
@@ -34,7 +34,7 @@ export function renderMSABlock({
34
34
  rowHeight,
35
35
  fontSize,
36
36
  highResScaleFactor,
37
- featureMode,
37
+ showDomains,
38
38
  } = model
39
39
  const k = highResScaleFactorOverride || highResScaleFactor
40
40
  const bx = blockSizeXOverride || blockSize
@@ -53,7 +53,7 @@ export function renderMSABlock({
53
53
  const xEnd = Math.max(0, Math.ceil((offsetX + bx) / colWidth))
54
54
  const visibleLeaves = leaves.slice(yStart, yEnd)
55
55
 
56
- if (!featureMode) {
56
+ if (!showDomains) {
57
57
  drawTiles({
58
58
  model,
59
59
  ctx,
@@ -152,7 +152,7 @@ function drawText({
152
152
  xStart: number
153
153
  xEnd: number
154
154
  }) {
155
- const { bgColor, featureMode, colorScheme, columns, colWidth, rowHeight } =
155
+ const { bgColor, showDomains, colorScheme, columns, colWidth, rowHeight } =
156
156
  model
157
157
  if (rowHeight >= 5 && colWidth > rowHeight / 2) {
158
158
  for (const node of visibleLeaves) {
@@ -168,7 +168,7 @@ function drawText({
168
168
  const x = i * colWidth + offsetX - (offsetX % colWidth)
169
169
 
170
170
  // note: -rowHeight/4 matches +rowHeight/4 in tree
171
- ctx.fillStyle = featureMode
171
+ ctx.fillStyle = showDomains
172
172
  ? 'black'
173
173
  : bgColor
174
174
  ? contrast
package/src/fetchUtils.ts CHANGED
@@ -15,9 +15,9 @@ export async function textfetch(url: string, args?: RequestInit) {
15
15
  return response.text()
16
16
  }
17
17
 
18
- export async function jsonfetch(url: string, args?: RequestInit) {
18
+ export async function jsonfetch<T>(url: string, args?: RequestInit) {
19
19
  const response = await myfetch(url, args)
20
- return response.json()
20
+ return response.json() as T
21
21
  }
22
22
 
23
23
  export async function arraybufferfetch(url: string) {
@@ -1,4 +1,6 @@
1
+ import { getSession } from '@jbrowse/core/util'
1
2
  import { jsonfetch, textfetch, timeout } from './fetchUtils'
3
+ import { MsaViewModel } from './model'
2
4
 
3
5
  const base = `https://www.ebi.ac.uk/Tools/services/rest`
4
6
 
@@ -24,11 +26,13 @@ async function runInterProScan({
24
26
  onProgress,
25
27
  onJobId,
26
28
  programs,
29
+ model,
27
30
  }: {
28
31
  seq: string
29
32
  programs: string[]
30
33
  onProgress: (arg?: { msg: string; url?: string }) => void
31
- onJobId: (arg: string) => void
34
+ onJobId?: (arg: string) => void
35
+ model: MsaViewModel
32
36
  }) {
33
37
  const jobId = await textfetch(`${base}/iprscan5/run`, {
34
38
  method: 'POST',
@@ -38,18 +42,18 @@ async function runInterProScan({
38
42
  programs: programs.join(','),
39
43
  }),
40
44
  })
41
- onJobId(jobId)
45
+ onJobId?.(jobId)
42
46
  await wait({
43
47
  jobId,
44
48
  onProgress,
45
49
  })
46
- return loadInterProScanResults(jobId)
50
+ return loadInterProScanResultsWithStatus({ jobId, model })
47
51
  }
48
52
 
49
- export async function loadInterProScanResults(jobId: string) {
50
- return (await jsonfetch(
53
+ export function loadInterProScanResults(jobId: string) {
54
+ return jsonfetch<InterProScanResponse>(
51
55
  `${base}/iprscan5/result/${jobId}/json`,
52
- )) as InterProScanResponse
56
+ )
53
57
  }
54
58
 
55
59
  async function wait({
@@ -60,17 +64,20 @@ async function wait({
60
64
  onProgress: (arg?: { msg: string; url?: string }) => void
61
65
  }) {
62
66
  const url = `${base}/iprscan5/status/${jobId}`
63
- // eslint-disable-next-line no-constant-condition
64
- while (true) {
65
- for (let i = 0; i < 10; i++) {
66
- await timeout(1000)
67
- onProgress({ msg: `Checking status... ${10 - i}`, url })
68
- }
69
- const result = await textfetch(url)
70
-
71
- if (result === 'FINISHED') {
72
- break
67
+ try {
68
+ // eslint-disable-next-line no-constant-condition
69
+ while (true) {
70
+ for (let i = 0; i < 10; i++) {
71
+ await timeout(1000)
72
+ onProgress({ msg: `Checking status ${10 - i}`, url })
73
+ }
74
+ const result = await textfetch(url)
75
+ if (result.includes('FINISHED')) {
76
+ break
77
+ }
73
78
  }
79
+ } finally {
80
+ onProgress()
74
81
  }
75
82
  }
76
83
 
@@ -80,19 +87,59 @@ export async function launchInterProScan({
80
87
  programs,
81
88
  onJobId,
82
89
  onProgress,
90
+ model,
83
91
  }: {
84
92
  algorithm: string
85
93
  seq: string
86
94
  programs: string[]
87
95
  onProgress: (arg?: { msg: string; url?: string }) => void
88
- onJobId: (arg: string) => void
96
+ onJobId?: (arg: string) => void
97
+ model: MsaViewModel
89
98
  }) {
90
- onProgress({ msg: `Launching ${algorithm} MSA...` })
91
- if (algorithm === 'interproscan') {
92
- const result = await runInterProScan({ seq, onJobId, onProgress, programs })
99
+ try {
100
+ onProgress({ msg: `Launching ${algorithm} MSA` })
101
+ if (algorithm === 'interproscan') {
102
+ const result = await runInterProScan({
103
+ seq,
104
+ onJobId,
105
+ onProgress,
106
+ programs,
107
+ model,
108
+ })
109
+ return result
110
+ } else {
111
+ throw new Error('unknown algorithm')
112
+ }
113
+ } finally {
93
114
  onProgress()
94
- return result
95
- } else {
96
- throw new Error('unknown algorithm')
115
+ }
116
+ }
117
+
118
+ export async function loadInterProScanResultsWithStatus({
119
+ jobId,
120
+ model,
121
+ }: {
122
+ jobId: string
123
+ model: MsaViewModel
124
+ }) {
125
+ try {
126
+ model.setStatus({
127
+ msg:
128
+ 'Downloading results of ' +
129
+ jobId +
130
+ ' (for larger sequences this can be slow, click status to download and upload in the manual tab)',
131
+ url: `https://www.ebi.ac.uk/Tools/services/rest/iprscan5/result/${jobId}/json`,
132
+ })
133
+ const ret = await loadInterProScanResults(jobId)
134
+ model.setLoadedInterProAnnotations(
135
+ Object.fromEntries(ret.results.map(r => [r.xref[0].id, r])),
136
+ )
137
+ model.setShowDomains(true)
138
+ getSession(model).notify(`Loaded interproscan ${jobId} results`, 'success')
139
+ } catch (e) {
140
+ console.error(e)
141
+ getSession(model).notifyError(`${e}`, e)
142
+ } finally {
143
+ model.setStatus()
97
144
  }
98
145
  }