@apollo-annotation/jbrowse-plugin-apollo 0.1.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 +76 -0
- package/dist/index.esm.js +10248 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +7 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
- package/package.json +130 -0
- package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
- package/src/ApolloInternetAccount/configSchema.ts +26 -0
- package/src/ApolloInternetAccount/index.ts +2 -0
- package/src/ApolloInternetAccount/model.ts +448 -0
- package/src/ApolloJobModel.ts +117 -0
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
- package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
- package/src/ApolloSequenceAdapter/index.ts +21 -0
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
- package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
- package/src/ApolloSixFrameRenderer/index.ts +3 -0
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
- package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
- package/src/ApolloTextSearchAdapter/index.ts +18 -0
- package/src/BackendDrivers/BackendDriver.ts +31 -0
- package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
- package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
- package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
- package/src/BackendDrivers/index.ts +4 -0
- package/src/ChangeManager.ts +148 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
- package/src/LinearApolloDisplay/components/index.ts +1 -0
- package/src/LinearApolloDisplay/configSchema.ts +16 -0
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
- package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
- package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
- package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
- package/src/LinearApolloDisplay/index.ts +2 -0
- package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
- package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
- package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
- package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
- package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
- package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
- package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
- package/src/LinearApolloDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
- package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
- package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
- package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
- package/src/OntologyManager/OntologyStore/index.ts +526 -0
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
- package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
- package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
- package/src/OntologyManager/index.ts +173 -0
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
- package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
- package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
- package/src/SixFrameFeatureDisplay/index.ts +2 -0
- package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
- package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
- package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
- package/src/TabularEditor/HybridGrid/index.ts +1 -0
- package/src/TabularEditor/TabularEditorPane.tsx +34 -0
- package/src/TabularEditor/index.ts +3 -0
- package/src/TabularEditor/model.ts +44 -0
- package/src/TabularEditor/types.ts +3 -0
- package/src/components/AddAssembly.tsx +464 -0
- package/src/components/AddChildFeature.tsx +247 -0
- package/src/components/AddFeature.tsx +252 -0
- package/src/components/CopyFeature.tsx +328 -0
- package/src/components/DeleteAssembly.tsx +185 -0
- package/src/components/DeleteFeature.tsx +90 -0
- package/src/components/Dialog.tsx +47 -0
- package/src/components/DownloadGFF3.tsx +213 -0
- package/src/components/ImportFeatures.tsx +295 -0
- package/src/components/ManageChecks.tsx +280 -0
- package/src/components/ManageUsers.tsx +218 -0
- package/src/components/ModifyFeatureAttribute.tsx +457 -0
- package/src/components/OntologyTermAutocomplete.tsx +240 -0
- package/src/components/OntologyTermMultiSelect.tsx +349 -0
- package/src/components/OpenLocalFile.tsx +178 -0
- package/src/components/ViewChangeLog.tsx +208 -0
- package/src/components/ViewCheckResults.tsx +151 -0
- package/src/components/index.ts +12 -0
- package/src/config.ts +10 -0
- package/src/declare.d.ts +3 -0
- package/src/extensions/annotationFromPileup.ts +208 -0
- package/src/extensions/index.ts +1 -0
- package/src/index.ts +394 -0
- package/src/makeDisplayComponent.tsx +244 -0
- package/src/session/ClientDataStore.ts +282 -0
- package/src/session/index.ts +1 -0
- package/src/session/session.ts +373 -0
- package/src/types.ts +10 -0
- package/src/util/index.ts +31 -0
- package/src/util/loadAssemblyIntoClient.ts +291 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import gff, { GFF3Item } from '@gmod/gff'
|
|
2
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
3
|
+
import { getConf } from '@jbrowse/core/configuration'
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogContentText,
|
|
9
|
+
MenuItem,
|
|
10
|
+
Select,
|
|
11
|
+
SelectChangeEvent,
|
|
12
|
+
} from '@mui/material'
|
|
13
|
+
import { ApolloAssembly } from 'apollo-mst'
|
|
14
|
+
import { makeGFF3Feature } from 'apollo-shared'
|
|
15
|
+
import { saveAs } from 'file-saver'
|
|
16
|
+
import { IMSTMap, getSnapshot } from 'mobx-state-tree'
|
|
17
|
+
import React, { useState } from 'react'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
ApolloInternetAccount,
|
|
21
|
+
CollaborationServerDriver,
|
|
22
|
+
InMemoryFileDriver,
|
|
23
|
+
} from '../BackendDrivers'
|
|
24
|
+
import { ApolloSessionModel } from '../session'
|
|
25
|
+
import { createFetchErrorMessage } from '../util'
|
|
26
|
+
import { Dialog } from './Dialog'
|
|
27
|
+
|
|
28
|
+
interface DownloadGFF3Props {
|
|
29
|
+
session: ApolloSessionModel
|
|
30
|
+
handleClose(): void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function DownloadGFF3({ handleClose, session }: DownloadGFF3Props) {
|
|
34
|
+
const [selectedAssembly, setSelectedAssembly] = useState<Assembly>()
|
|
35
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
36
|
+
|
|
37
|
+
const { collaborationServerDriver, getInternetAccount, inMemoryFileDriver } =
|
|
38
|
+
session.apolloDataStore as {
|
|
39
|
+
collaborationServerDriver: CollaborationServerDriver
|
|
40
|
+
inMemoryFileDriver: InMemoryFileDriver
|
|
41
|
+
getInternetAccount(
|
|
42
|
+
assemblyName?: string,
|
|
43
|
+
internetAccountId?: string,
|
|
44
|
+
): ApolloInternetAccount
|
|
45
|
+
}
|
|
46
|
+
const assemblies = [
|
|
47
|
+
...collaborationServerDriver.getAssemblies(),
|
|
48
|
+
...inMemoryFileDriver.getAssemblies(),
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
function handleChangeAssembly(e: SelectChangeEvent<string>) {
|
|
52
|
+
const newAssembly = assemblies.find((asm) => asm.name === e.target.value)
|
|
53
|
+
setSelectedAssembly(newAssembly)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
57
|
+
event.preventDefault()
|
|
58
|
+
setErrorMessage('')
|
|
59
|
+
if (!selectedAssembly) {
|
|
60
|
+
setErrorMessage('Must select assembly to download')
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { internetAccountConfigId } = getConf(selectedAssembly, [
|
|
65
|
+
'sequence',
|
|
66
|
+
'metadata',
|
|
67
|
+
]) as { internetAccountConfigId?: string }
|
|
68
|
+
if (internetAccountConfigId) {
|
|
69
|
+
await exportFromCollaborationServer(internetAccountConfigId)
|
|
70
|
+
} else {
|
|
71
|
+
exportFromMemory(session)
|
|
72
|
+
}
|
|
73
|
+
handleClose()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function exportFromCollaborationServer(
|
|
77
|
+
internetAccountConfigId: string,
|
|
78
|
+
) {
|
|
79
|
+
if (!selectedAssembly) {
|
|
80
|
+
setErrorMessage('Must select assembly to download')
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
const internetAccount = getInternetAccount(
|
|
84
|
+
selectedAssembly.configuration.name,
|
|
85
|
+
internetAccountConfigId,
|
|
86
|
+
)
|
|
87
|
+
const url = new URL('export/getID', internetAccount.baseURL)
|
|
88
|
+
const searchParams = new URLSearchParams({
|
|
89
|
+
assembly: selectedAssembly.name,
|
|
90
|
+
})
|
|
91
|
+
url.search = searchParams.toString()
|
|
92
|
+
const uri = url.toString()
|
|
93
|
+
const apolloFetch = internetAccount.getFetcher({
|
|
94
|
+
locationType: 'UriLocation',
|
|
95
|
+
uri,
|
|
96
|
+
})
|
|
97
|
+
const response = await apolloFetch(uri, { method: 'GET' })
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
const newErrorMessage = await createFetchErrorMessage(
|
|
100
|
+
response,
|
|
101
|
+
'Error when exporting ID',
|
|
102
|
+
)
|
|
103
|
+
setErrorMessage(newErrorMessage)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
const { exportID } = (await response.json()) as { exportID: string }
|
|
107
|
+
|
|
108
|
+
const exportURL = new URL('export', internetAccount.baseURL)
|
|
109
|
+
const exportSearchParams = new URLSearchParams({ exportID })
|
|
110
|
+
exportURL.search = exportSearchParams.toString()
|
|
111
|
+
const exportUri = exportURL.toString()
|
|
112
|
+
|
|
113
|
+
window.open(exportUri, '_blank')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function exportFromMemory(session: ApolloSessionModel) {
|
|
117
|
+
if (!selectedAssembly) {
|
|
118
|
+
setErrorMessage('Must select assembly to download')
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
const { assemblies } = session.apolloDataStore as {
|
|
122
|
+
assemblies: IMSTMap<typeof ApolloAssembly>
|
|
123
|
+
}
|
|
124
|
+
const assembly = assemblies.get(selectedAssembly.name)
|
|
125
|
+
const refSeqs = assembly?.refSeqs
|
|
126
|
+
if (!refSeqs) {
|
|
127
|
+
setErrorMessage(
|
|
128
|
+
`No refSeqs found for assembly "${selectedAssembly.name}"`,
|
|
129
|
+
)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
const gff3Items: GFF3Item[] = [{ directive: 'gff-version', value: '3' }]
|
|
133
|
+
const sequenceFeatures = getConf(selectedAssembly, [
|
|
134
|
+
'sequence',
|
|
135
|
+
'adapter',
|
|
136
|
+
'features',
|
|
137
|
+
]) as { refName: string; start: number; end: number; seq: string }[]
|
|
138
|
+
for (const sequenceFeature of sequenceFeatures) {
|
|
139
|
+
const { end, refName, start } = sequenceFeature
|
|
140
|
+
gff3Items.push({
|
|
141
|
+
directive: 'sequence-region',
|
|
142
|
+
value: `${refName} ${start + 1} ${end}`,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
for (const [, refSeq] of refSeqs) {
|
|
146
|
+
const features = refSeq?.features
|
|
147
|
+
if (!features) {
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
for (const [, feature] of features) {
|
|
151
|
+
gff3Items.push(makeGFF3Feature(getSnapshot(feature)))
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const sequenceFeature of sequenceFeatures) {
|
|
155
|
+
const { refName, seq } = sequenceFeature
|
|
156
|
+
gff3Items.push({ id: refName, description: '', sequence: seq })
|
|
157
|
+
}
|
|
158
|
+
const gff3 = gff.formatSync(gff3Items)
|
|
159
|
+
const gff3Blob = new Blob([gff3], { type: 'text/plain;charset=utf-8' })
|
|
160
|
+
saveAs(
|
|
161
|
+
gff3Blob,
|
|
162
|
+
`${selectedAssembly.displayName ?? selectedAssembly.name}.gff3`,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<Dialog
|
|
168
|
+
open
|
|
169
|
+
title="Export GFF3"
|
|
170
|
+
handleClose={handleClose}
|
|
171
|
+
maxWidth={false}
|
|
172
|
+
data-testid="download-gff3"
|
|
173
|
+
>
|
|
174
|
+
<form onSubmit={onSubmit}>
|
|
175
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
176
|
+
<DialogContentText>Select assembly</DialogContentText>
|
|
177
|
+
<Select
|
|
178
|
+
labelId="label"
|
|
179
|
+
value={selectedAssembly?.name ?? ''}
|
|
180
|
+
onChange={handleChangeAssembly}
|
|
181
|
+
disabled={assemblies.length === 0}
|
|
182
|
+
>
|
|
183
|
+
{assemblies.map((option) => (
|
|
184
|
+
<MenuItem key={option.name} value={option.name}>
|
|
185
|
+
{option.displayName ?? option.name}
|
|
186
|
+
</MenuItem>
|
|
187
|
+
))}
|
|
188
|
+
</Select>
|
|
189
|
+
<DialogContentText>
|
|
190
|
+
Select assembly to export to GFF3
|
|
191
|
+
</DialogContentText>
|
|
192
|
+
</DialogContent>
|
|
193
|
+
<DialogActions>
|
|
194
|
+
<Button
|
|
195
|
+
disabled={!selectedAssembly}
|
|
196
|
+
variant="contained"
|
|
197
|
+
type="submit"
|
|
198
|
+
>
|
|
199
|
+
Download
|
|
200
|
+
</Button>
|
|
201
|
+
<Button variant="outlined" type="submit" onClick={handleClose}>
|
|
202
|
+
Cancel
|
|
203
|
+
</Button>
|
|
204
|
+
</DialogActions>
|
|
205
|
+
</form>
|
|
206
|
+
{errorMessage ? (
|
|
207
|
+
<DialogContent>
|
|
208
|
+
<DialogContentText color="error">{errorMessage}</DialogContentText>
|
|
209
|
+
</DialogContent>
|
|
210
|
+
) : null}
|
|
211
|
+
</Dialog>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
2
|
+
import { getConf } from '@jbrowse/core/configuration'
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
DialogActions,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogContentText,
|
|
8
|
+
MenuItem,
|
|
9
|
+
Select,
|
|
10
|
+
SelectChangeEvent,
|
|
11
|
+
} from '@mui/material'
|
|
12
|
+
import Checkbox from '@mui/material/Checkbox'
|
|
13
|
+
import FormControlLabel from '@mui/material/FormControlLabel'
|
|
14
|
+
import LinearProgress from '@mui/material/LinearProgress'
|
|
15
|
+
import { AddFeaturesFromFileChange } from 'apollo-shared'
|
|
16
|
+
import React, { useEffect, useState } from 'react'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
ApolloInternetAccount,
|
|
20
|
+
CollaborationServerDriver,
|
|
21
|
+
} from '../BackendDrivers'
|
|
22
|
+
import { ChangeManager } from '../ChangeManager'
|
|
23
|
+
import { ApolloSessionModel } from '../session'
|
|
24
|
+
import { createFetchErrorMessage } from '../util'
|
|
25
|
+
import { Dialog } from './Dialog'
|
|
26
|
+
|
|
27
|
+
interface ImportFeaturesProps {
|
|
28
|
+
session: ApolloSessionModel
|
|
29
|
+
handleClose(): void
|
|
30
|
+
changeManager: ChangeManager
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ImportFeatures({
|
|
34
|
+
changeManager,
|
|
35
|
+
handleClose,
|
|
36
|
+
session,
|
|
37
|
+
}: ImportFeaturesProps) {
|
|
38
|
+
const { apolloDataStore } = session
|
|
39
|
+
|
|
40
|
+
const [file, setFile] = useState<File>()
|
|
41
|
+
const [selectedAssembly, setSelectedAssembly] = useState<Assembly>()
|
|
42
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
43
|
+
const [submitted, setSubmitted] = useState(false)
|
|
44
|
+
// default is -1, submit button should be disabled until count is set
|
|
45
|
+
const [featuresCount, setFeaturesCount] = useState<number | undefined>()
|
|
46
|
+
const [deleteFeatures, setDeleteFeatures] = useState(false)
|
|
47
|
+
const [loading, setLoading] = useState(false)
|
|
48
|
+
|
|
49
|
+
const { collaborationServerDriver, getInternetAccount } = apolloDataStore as {
|
|
50
|
+
collaborationServerDriver: CollaborationServerDriver
|
|
51
|
+
getInternetAccount(
|
|
52
|
+
assemblyName?: string,
|
|
53
|
+
internetAccountId?: string,
|
|
54
|
+
): ApolloInternetAccount
|
|
55
|
+
}
|
|
56
|
+
const assemblies = collaborationServerDriver.getAssemblies()
|
|
57
|
+
|
|
58
|
+
function handleChangeAssembly(e: SelectChangeEvent<string>) {
|
|
59
|
+
const newAssembly = assemblies.find((asm) => asm.name === e.target.value)
|
|
60
|
+
setSelectedAssembly(newAssembly)
|
|
61
|
+
setSubmitted(false)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handleDeleteFeatures(e: React.ChangeEvent<HTMLInputElement>) {
|
|
65
|
+
setDeleteFeatures(e.target.checked)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// fetch and set features count for selected assembly
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!selectedAssembly) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
const updateFeaturesCount = async () => {
|
|
74
|
+
// TODO: this code will not work for running on desktop
|
|
75
|
+
const { internetAccountConfigId } = getConf(selectedAssembly, [
|
|
76
|
+
'sequence',
|
|
77
|
+
'metadata',
|
|
78
|
+
]) as { internetAccountConfigId?: string }
|
|
79
|
+
const apolloInternetAccount = getInternetAccount(
|
|
80
|
+
selectedAssembly.name,
|
|
81
|
+
internetAccountConfigId,
|
|
82
|
+
)
|
|
83
|
+
if (!apolloInternetAccount) {
|
|
84
|
+
throw new Error('No Apollo internet account found')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { baseURL } = apolloInternetAccount
|
|
88
|
+
const uri = new URL('/features/count', baseURL)
|
|
89
|
+
const searchParams = new URLSearchParams({
|
|
90
|
+
assemblyId: selectedAssembly.name,
|
|
91
|
+
})
|
|
92
|
+
uri.search = searchParams.toString()
|
|
93
|
+
const fetch = apolloInternetAccount?.getFetcher({
|
|
94
|
+
locationType: 'UriLocation',
|
|
95
|
+
uri: uri.toString(),
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
setLoading(true)
|
|
99
|
+
const response = await fetch(uri.toString(), { method: 'GET' })
|
|
100
|
+
|
|
101
|
+
if (response.ok) {
|
|
102
|
+
const countObj = (await response.json()) as { count: number }
|
|
103
|
+
setFeaturesCount(countObj.count)
|
|
104
|
+
} else {
|
|
105
|
+
throw new Error(await createFetchErrorMessage(response))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setLoading(false)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
updateFeaturesCount().catch((error) => {
|
|
112
|
+
console.error(error)
|
|
113
|
+
setErrorMessage(error.message ?? error)
|
|
114
|
+
})
|
|
115
|
+
}, [getInternetAccount, session, selectedAssembly])
|
|
116
|
+
|
|
117
|
+
function handleChangeFile(e: React.ChangeEvent<HTMLInputElement>) {
|
|
118
|
+
setSubmitted(false)
|
|
119
|
+
if (!e.target.files) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
setFile(e.target.files[0])
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
126
|
+
event.preventDefault()
|
|
127
|
+
setErrorMessage('')
|
|
128
|
+
setLoading(true)
|
|
129
|
+
setSubmitted(true)
|
|
130
|
+
|
|
131
|
+
// let fileChecksum = ''
|
|
132
|
+
let fileId = ''
|
|
133
|
+
|
|
134
|
+
if (!file) {
|
|
135
|
+
setErrorMessage('must select a file')
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!selectedAssembly) {
|
|
140
|
+
setErrorMessage('Must select assembly to download')
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { internetAccountConfigId } = getConf(selectedAssembly, [
|
|
145
|
+
'sequence',
|
|
146
|
+
'metadata',
|
|
147
|
+
]) as { internetAccountConfigId?: string }
|
|
148
|
+
const apolloInternetAccount = getInternetAccount(
|
|
149
|
+
selectedAssembly.name,
|
|
150
|
+
internetAccountConfigId,
|
|
151
|
+
)
|
|
152
|
+
const { baseURL } = apolloInternetAccount
|
|
153
|
+
|
|
154
|
+
// First upload file
|
|
155
|
+
const url = new URL('/files', baseURL).href
|
|
156
|
+
const formData = new FormData()
|
|
157
|
+
formData.append('file', file)
|
|
158
|
+
formData.append('fileName', file.name)
|
|
159
|
+
formData.append('type', 'text/x-gff3')
|
|
160
|
+
const apolloFetchFile = apolloInternetAccount?.getFetcher({
|
|
161
|
+
locationType: 'UriLocation',
|
|
162
|
+
uri: url,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
handleClose()
|
|
166
|
+
|
|
167
|
+
const { jobsManager } = session
|
|
168
|
+
const controller = new AbortController()
|
|
169
|
+
|
|
170
|
+
const job = {
|
|
171
|
+
name: `Importing features for ${selectedAssembly.displayName}`,
|
|
172
|
+
statusMessage: 'Uploading file, this may take awhile',
|
|
173
|
+
progressPct: 0,
|
|
174
|
+
cancelCallback: () => {
|
|
175
|
+
controller.abort()
|
|
176
|
+
jobsManager.abortJob(job.name)
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
jobsManager.runJob(job)
|
|
181
|
+
|
|
182
|
+
if (apolloFetchFile) {
|
|
183
|
+
const { signal } = controller
|
|
184
|
+
const response = await apolloFetchFile(url, {
|
|
185
|
+
method: 'POST',
|
|
186
|
+
body: formData,
|
|
187
|
+
signal,
|
|
188
|
+
})
|
|
189
|
+
if (!response.ok) {
|
|
190
|
+
const newErrorMessage = await createFetchErrorMessage(
|
|
191
|
+
response,
|
|
192
|
+
'Error when inserting new features (while uploading file)',
|
|
193
|
+
)
|
|
194
|
+
jobsManager.abortJob(job.name, newErrorMessage)
|
|
195
|
+
setErrorMessage(newErrorMessage)
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
const result = await response.json()
|
|
199
|
+
// fileChecksum = result.checksum
|
|
200
|
+
fileId = result._id
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Add features
|
|
204
|
+
const change = new AddFeaturesFromFileChange({
|
|
205
|
+
typeName: 'AddFeaturesFromFileChange',
|
|
206
|
+
assembly: selectedAssembly.name,
|
|
207
|
+
fileId,
|
|
208
|
+
deleteExistingFeatures: deleteFeatures,
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
jobsManager.done(job)
|
|
212
|
+
|
|
213
|
+
await changeManager.submit(change, { updateJobsManager: true })
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<Dialog
|
|
218
|
+
open
|
|
219
|
+
title="Import Features from GFF3 file"
|
|
220
|
+
handleClose={handleClose}
|
|
221
|
+
maxWidth={false}
|
|
222
|
+
data-testid="import-features-dialog"
|
|
223
|
+
>
|
|
224
|
+
{loading ? <LinearProgress /> : null}
|
|
225
|
+
|
|
226
|
+
<form onSubmit={onSubmit}>
|
|
227
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
228
|
+
<DialogContentText>Select assembly</DialogContentText>
|
|
229
|
+
<Select
|
|
230
|
+
labelId="label"
|
|
231
|
+
value={selectedAssembly?.name ?? ''}
|
|
232
|
+
onChange={handleChangeAssembly}
|
|
233
|
+
disabled={submitted && !errorMessage}
|
|
234
|
+
>
|
|
235
|
+
{assemblies.map((option) => (
|
|
236
|
+
<MenuItem key={option.name} value={option.name}>
|
|
237
|
+
{option.displayName ?? option.name}
|
|
238
|
+
</MenuItem>
|
|
239
|
+
))}
|
|
240
|
+
</Select>
|
|
241
|
+
</DialogContent>
|
|
242
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
243
|
+
<DialogContentText>Upload GFF3 to load features</DialogContentText>
|
|
244
|
+
<input
|
|
245
|
+
type="file"
|
|
246
|
+
onChange={handleChangeFile}
|
|
247
|
+
disabled={submitted && !errorMessage}
|
|
248
|
+
/>
|
|
249
|
+
</DialogContent>
|
|
250
|
+
|
|
251
|
+
{featuresCount && featuresCount > 0 ? (
|
|
252
|
+
<DialogContent>
|
|
253
|
+
<DialogContentText>
|
|
254
|
+
This assembly already has {featuresCount} features, would you like
|
|
255
|
+
to delete the existing features before importing new ones?
|
|
256
|
+
</DialogContentText>
|
|
257
|
+
<FormControlLabel
|
|
258
|
+
label="Yes, delete existing features"
|
|
259
|
+
disabled={submitted && !errorMessage}
|
|
260
|
+
control={
|
|
261
|
+
<Checkbox
|
|
262
|
+
checked={deleteFeatures}
|
|
263
|
+
onChange={handleDeleteFeatures}
|
|
264
|
+
inputProps={{ 'aria-label': 'controlled' }}
|
|
265
|
+
color="warning"
|
|
266
|
+
/>
|
|
267
|
+
}
|
|
268
|
+
/>
|
|
269
|
+
</DialogContent>
|
|
270
|
+
) : null}
|
|
271
|
+
|
|
272
|
+
<DialogActions>
|
|
273
|
+
<Button
|
|
274
|
+
disabled={
|
|
275
|
+
!(selectedAssembly && file && featuresCount !== undefined) ||
|
|
276
|
+
submitted
|
|
277
|
+
}
|
|
278
|
+
variant="contained"
|
|
279
|
+
type="submit"
|
|
280
|
+
>
|
|
281
|
+
{submitted ? 'Submitting...' : 'Submit'}
|
|
282
|
+
</Button>
|
|
283
|
+
<Button variant="outlined" type="submit" onClick={handleClose}>
|
|
284
|
+
Close
|
|
285
|
+
</Button>
|
|
286
|
+
</DialogActions>
|
|
287
|
+
</form>
|
|
288
|
+
{errorMessage ? (
|
|
289
|
+
<DialogContent>
|
|
290
|
+
<DialogContentText color="error">{errorMessage}</DialogContentText>
|
|
291
|
+
</DialogContent>
|
|
292
|
+
) : null}
|
|
293
|
+
</Dialog>
|
|
294
|
+
)
|
|
295
|
+
}
|