@apollo-annotation/jbrowse-plugin-apollo 0.3.4 → 0.3.6
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/dist/index.esm.js +5466 -4490
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +5283 -4318
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +6806 -4088
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/ApolloInternetAccount/addMenuItems.ts +5 -2
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -0
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +1 -1
- package/src/ApolloInternetAccount/components/LoginIcons.tsx +1 -1
- package/src/ApolloInternetAccount/configSchema.ts +1 -1
- package/src/ApolloInternetAccount/model.ts +11 -10
- package/src/ApolloJobModel.ts +1 -1
- package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +8 -6
- package/src/ApolloRefNameAliasAdapter/index.ts +2 -2
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +17 -4
- package/src/ApolloSequenceAdapter/index.ts +1 -1
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +8 -7
- package/src/ApolloTextSearchAdapter/index.ts +1 -1
- package/src/BackendDrivers/BackendDriver.ts +7 -7
- package/src/BackendDrivers/CollaborationServerDriver.ts +14 -10
- package/src/BackendDrivers/DesktopFileDriver.ts +11 -10
- package/src/BackendDrivers/InMemoryFileDriver.ts +10 -6
- package/src/ChangeManager.ts +5 -5
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +92 -20
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +170 -27
- package/src/FeatureDetailsWidget/AttributeKey.tsx +50 -0
- package/src/FeatureDetailsWidget/AttributeKeySelector.tsx +104 -0
- package/src/FeatureDetailsWidget/Attributes.tsx +213 -320
- package/src/FeatureDetailsWidget/BasicInformation.tsx +8 -9
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +104 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +22 -0
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +10 -8
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/Sequence.tsx +18 -35
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -1
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +140 -95
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
- package/src/FeatureDetailsWidget/model.ts +8 -3
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +19 -12
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +19 -41
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +44 -22
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +6 -5
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -7
- package/src/LinearApolloDisplay/stateModel/base.ts +52 -10
- package/src/LinearApolloDisplay/stateModel/index.ts +4 -3
- package/src/LinearApolloDisplay/stateModel/layouts.ts +8 -34
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +13 -12
- package/src/LinearApolloDisplay/stateModel/rendering.ts +63 -31
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +221 -0
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +40 -0
- package/src/LinearApolloSixFrameDisplay/components/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/configSchema.ts +7 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +821 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +63 -0
- package/src/LinearApolloSixFrameDisplay/glyphs/index.ts +1 -0
- package/src/LinearApolloSixFrameDisplay/index.ts +2 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +261 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/index.ts +27 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +236 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +349 -0
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +199 -0
- package/src/LinearApolloSixFrameDisplay/types.ts +1 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
- package/src/OntologyManager/OntologyStore/fulltext.test.ts +1 -1
- package/src/OntologyManager/OntologyStore/fulltext.ts +8 -3
- package/src/OntologyManager/OntologyStore/index.test.ts +4 -1
- package/src/OntologyManager/OntologyStore/index.ts +19 -14
- package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +6 -5
- package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +11 -5
- package/src/OntologyManager/index.ts +10 -6
- package/src/OntologyManager/util.ts +3 -2
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/Feature.tsx +9 -8
- package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +1 -1
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +3 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +15 -13
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +9 -33
- package/src/TabularEditor/TabularEditorPane.tsx +1 -1
- package/src/TabularEditor/model.ts +2 -2
- package/src/TabularEditor/types.ts +5 -2
- package/src/components/AddAssembly.tsx +611 -291
- package/src/components/AddChildFeature.tsx +6 -5
- package/src/components/AddFeature.tsx +211 -38
- package/src/components/AddRefSeqAliases.tsx +14 -12
- package/src/components/CopyFeature.tsx +8 -7
- package/src/components/CreateApolloAnnotation.tsx +154 -46
- package/src/components/DeleteAssembly.tsx +9 -8
- package/src/components/DeleteFeature.tsx +5 -4
- package/src/components/Dialog.tsx +1 -1
- package/src/components/DownloadGFF3.tsx +11 -10
- package/src/components/FilterFeatures.tsx +6 -4
- package/src/components/ImportFeatures.tsx +7 -6
- package/src/components/LogOut.tsx +5 -4
- package/src/components/ManageChecks.tsx +9 -8
- package/src/components/ManageUsers.tsx +11 -10
- package/src/components/OntologyTermAutocomplete.tsx +5 -5
- package/src/components/OntologyTermMultiSelect.tsx +9 -6
- package/src/components/OpenLocalFile.tsx +4 -3
- package/src/components/ViewChangeLog.tsx +7 -6
- package/src/components/ViewCheckResults.tsx +8 -7
- package/src/components/index.ts +0 -1
- package/src/extensions/annotationFromJBrowseFeature.test.ts +1 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +14 -12
- package/src/extensions/annotationFromPileup.ts +6 -6
- package/src/index.ts +33 -50
- package/src/makeDisplayComponent.tsx +93 -41
- package/src/session/ClientDataStore.ts +21 -17
- package/src/session/session.ts +20 -26
- package/src/types.ts +4 -4
- package/src/util/annotationFeatureUtils.ts +53 -0
- package/src/util/index.ts +4 -3
- package/src/util/loadAssemblyIntoClient.ts +10 -3
- package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +0 -13
- package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +0 -707
- package/src/ApolloSixFrameRenderer/configSchema.ts +0 -7
- package/src/ApolloSixFrameRenderer/index.ts +0 -3
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
- package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +0 -19
- package/src/SixFrameFeatureDisplay/components/index.ts +0 -1
- package/src/SixFrameFeatureDisplay/configSchema.ts +0 -21
- package/src/SixFrameFeatureDisplay/index.ts +0 -2
- package/src/SixFrameFeatureDisplay/stateModel.ts +0 -439
- package/src/components/ModifyFeatureAttribute.tsx +0 -460
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
|
|
2
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
3
2
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
4
3
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
@@ -10,38 +9,45 @@ import {
|
|
|
10
9
|
AddAssemblyFromFileChange,
|
|
11
10
|
} from '@apollo-annotation/shared'
|
|
12
11
|
import { readConfObject } from '@jbrowse/core/configuration'
|
|
13
|
-
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
12
|
+
import { type AbstractSessionModel } from '@jbrowse/core/util'
|
|
13
|
+
import InfoIcon from '@mui/icons-material/Info'
|
|
14
14
|
import LinkIcon from '@mui/icons-material/Link'
|
|
15
|
+
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'
|
|
16
|
+
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'
|
|
15
17
|
import {
|
|
18
|
+
Accordion,
|
|
19
|
+
AccordionDetails,
|
|
20
|
+
AccordionSummary,
|
|
16
21
|
Box,
|
|
17
22
|
Button,
|
|
18
23
|
Checkbox,
|
|
19
24
|
DialogActions,
|
|
20
25
|
DialogContent,
|
|
21
26
|
DialogContentText,
|
|
22
|
-
FormControl,
|
|
23
27
|
FormControlLabel,
|
|
24
28
|
FormGroup,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
IconButton,
|
|
30
|
+
InputAdornment,
|
|
31
|
+
LinearProgress,
|
|
32
|
+
Table,
|
|
33
|
+
TableBody,
|
|
34
|
+
TableCell,
|
|
35
|
+
TableRow,
|
|
31
36
|
TextField,
|
|
37
|
+
Tooltip,
|
|
32
38
|
Typography,
|
|
33
39
|
} from '@mui/material'
|
|
34
|
-
import InputAdornment from '@mui/material/InputAdornment'
|
|
35
|
-
import LinearProgress from '@mui/material/LinearProgress'
|
|
36
40
|
import ObjectID from 'bson-objectid'
|
|
37
41
|
import { getRoot } from 'mobx-state-tree'
|
|
38
|
-
import React, { useState } from 'react'
|
|
42
|
+
import React, { useEffect, useState } from 'react'
|
|
43
|
+
import { makeStyles } from 'tss-react/mui'
|
|
39
44
|
|
|
40
|
-
import { ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
41
|
-
import { ChangeManager } from '../ChangeManager'
|
|
42
|
-
import { ApolloSessionModel } from '../session'
|
|
43
|
-
import { ApolloRootModel } from '../types'
|
|
45
|
+
import { type ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
46
|
+
import { type ChangeManager } from '../ChangeManager'
|
|
47
|
+
import { type ApolloSessionModel } from '../session'
|
|
48
|
+
import { type ApolloRootModel } from '../types'
|
|
44
49
|
import { createFetchErrorMessage } from '../util'
|
|
50
|
+
|
|
45
51
|
import { Dialog } from './Dialog'
|
|
46
52
|
|
|
47
53
|
interface AddAssemblyProps {
|
|
@@ -53,14 +59,71 @@ interface AddAssemblyProps {
|
|
|
53
59
|
enum FileType {
|
|
54
60
|
GFF3 = 'text/x-gff3',
|
|
55
61
|
FASTA = 'text/x-fasta',
|
|
62
|
+
BGZIP_FASTA = 'application/x-bgzip-fasta',
|
|
63
|
+
FAI = 'text/x-fai',
|
|
64
|
+
GZI = 'application/x-gzi',
|
|
56
65
|
EXTERNAL = 'text/x-external',
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
const useStyles = makeStyles()((theme) => ({
|
|
69
|
+
accordion: {
|
|
70
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
71
|
+
'&:not(:last-child)': {
|
|
72
|
+
borderBottom: 0,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
accordionSummary: {
|
|
76
|
+
flexDirection: 'row-reverse',
|
|
77
|
+
},
|
|
78
|
+
accordionDetails: {
|
|
79
|
+
padding: theme.spacing(2),
|
|
80
|
+
borderTop: '1px solid rgba(0, 0, 0, .125)',
|
|
81
|
+
},
|
|
82
|
+
radioIcon: {
|
|
83
|
+
color: theme?.palette?.tertiary?.contrastText,
|
|
84
|
+
},
|
|
85
|
+
dialog: {
|
|
86
|
+
// minHeight: 500,
|
|
87
|
+
minWidth: 550,
|
|
88
|
+
maxWidth: 800,
|
|
89
|
+
},
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
function checkSumbission(
|
|
93
|
+
validAsm: boolean,
|
|
94
|
+
sequenceIsEditable: boolean,
|
|
95
|
+
fileType: FileType,
|
|
96
|
+
fastaFile: File | null,
|
|
97
|
+
fastaIndexFile: File | null,
|
|
98
|
+
fastaGziIndexFile: File | null,
|
|
99
|
+
validFastaUrl: boolean,
|
|
100
|
+
validFastaIndexUrl: boolean,
|
|
101
|
+
validFastaGziIndexUrl: boolean,
|
|
102
|
+
) {
|
|
103
|
+
if (!validAsm) {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
if (sequenceIsEditable && fastaFile) {
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
if (fileType === FileType.GFF3 && fastaFile) {
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
if (fastaFile && fastaIndexFile && fastaGziIndexFile) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
if (validFastaUrl && validFastaIndexUrl && validFastaGziIndexUrl) {
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
59
121
|
export function AddAssembly({
|
|
60
122
|
changeManager,
|
|
61
123
|
handleClose,
|
|
62
124
|
session,
|
|
63
125
|
}: AddAssemblyProps) {
|
|
126
|
+
const { classes } = useStyles()
|
|
64
127
|
const { internetAccounts } = getRoot<ApolloRootModel>(session)
|
|
65
128
|
const { notify } = session as unknown as AbstractSessionModel
|
|
66
129
|
const apolloInternetAccounts = internetAccounts.filter(
|
|
@@ -72,62 +135,39 @@ export function AddAssembly({
|
|
|
72
135
|
const [assemblyName, setAssemblyName] = useState('')
|
|
73
136
|
const [errorMessage, setErrorMessage] = useState('')
|
|
74
137
|
const [validAsm, setValidAsm] = useState(false)
|
|
75
|
-
const [
|
|
76
|
-
const [fileType, setFileType] = useState(FileType.GFF3)
|
|
138
|
+
const [fileType, setFileType] = useState(FileType.BGZIP_FASTA)
|
|
77
139
|
const [importFeatures, setImportFeatures] = useState(true)
|
|
140
|
+
const [sequenceIsEditable, setSequenceIsEditable] = useState(false)
|
|
78
141
|
const [submitted, setSubmitted] = useState(false)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
const [
|
|
83
|
-
|
|
84
|
-
const [
|
|
142
|
+
|
|
143
|
+
const [fastaFile, setFastaFile] = useState<File | null>(null)
|
|
144
|
+
const [fastaIndexFile, setFastaIndexFile] = useState<File | null>(null)
|
|
145
|
+
const [fastaGziIndexFile, setFastaGziIndexFile] = useState<File | null>(null)
|
|
146
|
+
|
|
147
|
+
const [fastaUrl, setFastaUrl] = useState<string>('')
|
|
148
|
+
const [fastaIndexUrl, setFastaIndexUrl] = useState<string>('')
|
|
149
|
+
const [fastaGziIndexUrl, setFastaGziIndexUrl] = useState<string>('')
|
|
150
|
+
|
|
85
151
|
const [loading, setLoading] = useState(false)
|
|
152
|
+
const [isGzip, setIsGzip] = useState<boolean>(false)
|
|
86
153
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
(ia) => ia.internetAccountId === e.target.value,
|
|
91
|
-
)
|
|
92
|
-
if (!newlySelectedInternetAccount) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`Could not find internetAccount with ID "${e.target.value}"`,
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
setSelectedInternetAccount(newlySelectedInternetAccount)
|
|
98
|
-
}
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
setFastaIndexUrl(fastaUrl ? `${fastaUrl}.fai` : '')
|
|
156
|
+
}, [fastaUrl])
|
|
99
157
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
const selectedFile = e.target.files.item(0)
|
|
105
|
-
setFile(selectedFile)
|
|
106
|
-
if (!selectedFile) {
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
const fileNameLower = selectedFile.name.toLowerCase()
|
|
110
|
-
if (
|
|
111
|
-
fileNameLower.endsWith('.fasta') ||
|
|
112
|
-
fileNameLower.endsWith('.fna') ||
|
|
113
|
-
fileNameLower.endsWith('.fa')
|
|
114
|
-
) {
|
|
115
|
-
setFileType(FileType.FASTA)
|
|
116
|
-
} else if (
|
|
117
|
-
fileNameLower.endsWith('.gff3') ||
|
|
118
|
-
fileNameLower.endsWith('.gff')
|
|
119
|
-
) {
|
|
120
|
-
setFileType(FileType.GFF3)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
setFastaGziIndexUrl(fastaUrl ? `${fastaUrl}.gzi` : '')
|
|
160
|
+
}, [fastaUrl])
|
|
123
161
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (sequenceIsEditable || fileType === FileType.GFF3) {
|
|
164
|
+
setIsGzip(
|
|
165
|
+
fastaFile?.name.toLocaleLowerCase().endsWith('.gz') ? true : false,
|
|
166
|
+
)
|
|
167
|
+
} else {
|
|
168
|
+
setIsGzip(true)
|
|
169
|
+
}
|
|
170
|
+
}, [fastaFile, sequenceIsEditable, fileType])
|
|
131
171
|
|
|
132
172
|
function checkAssemblyName(assembly: string) {
|
|
133
173
|
const { assemblies } = session as unknown as AbstractSessionModel
|
|
@@ -143,6 +183,70 @@ export function AddAssembly({
|
|
|
143
183
|
}
|
|
144
184
|
}
|
|
145
185
|
|
|
186
|
+
async function uploadFile(file: File, fileType: FileType): Promise<string> {
|
|
187
|
+
const { jobsManager } = session
|
|
188
|
+
const controller = new AbortController()
|
|
189
|
+
|
|
190
|
+
const [{ baseURL, getFetcher }] = apolloInternetAccounts
|
|
191
|
+
const url = new URL('files', baseURL)
|
|
192
|
+
|
|
193
|
+
url.searchParams.set('type', fileType)
|
|
194
|
+
const uri = url.href
|
|
195
|
+
const formData = new FormData()
|
|
196
|
+
let filename = file.name
|
|
197
|
+
|
|
198
|
+
if (fileType === FileType.FAI || fileType === FileType.GZI) {
|
|
199
|
+
filename = `${filename}.txt`
|
|
200
|
+
} else if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
201
|
+
filename = `${filename}.gz`
|
|
202
|
+
} else if (!isGzip && file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
203
|
+
filename = `${filename}.txt`
|
|
204
|
+
}
|
|
205
|
+
formData.append('file', file, filename)
|
|
206
|
+
formData.append('type', fileType)
|
|
207
|
+
const apolloFetchFile = getFetcher({
|
|
208
|
+
locationType: 'UriLocation',
|
|
209
|
+
uri,
|
|
210
|
+
})
|
|
211
|
+
if (apolloFetchFile) {
|
|
212
|
+
const job = {
|
|
213
|
+
name: `UploadAssemblyFile for ${assemblyName}`,
|
|
214
|
+
statusMessage: 'Pre-validating',
|
|
215
|
+
progressPct: 0,
|
|
216
|
+
cancelCallback: () => {
|
|
217
|
+
controller.abort()
|
|
218
|
+
jobsManager.abortJob(job.name)
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
jobsManager.runJob(job)
|
|
222
|
+
jobsManager.update(
|
|
223
|
+
job.name,
|
|
224
|
+
`Uploading ${file.name}, this may take awhile`,
|
|
225
|
+
)
|
|
226
|
+
const { signal } = controller
|
|
227
|
+
|
|
228
|
+
const response = await apolloFetchFile(uri, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
body: formData,
|
|
231
|
+
signal,
|
|
232
|
+
})
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
const newErrorMessage = await createFetchErrorMessage(
|
|
235
|
+
response,
|
|
236
|
+
'Error when inserting new assembly (while uploading file)',
|
|
237
|
+
)
|
|
238
|
+
jobsManager.abortJob(job.name, newErrorMessage)
|
|
239
|
+
setErrorMessage(newErrorMessage)
|
|
240
|
+
return ''
|
|
241
|
+
}
|
|
242
|
+
const result = await response.json()
|
|
243
|
+
const fileId = result._id as string
|
|
244
|
+
jobsManager.done(job)
|
|
245
|
+
return fileId
|
|
246
|
+
}
|
|
247
|
+
throw new Error('Failed to fetch')
|
|
248
|
+
}
|
|
249
|
+
|
|
146
250
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
147
251
|
event.preventDefault()
|
|
148
252
|
setErrorMessage('')
|
|
@@ -153,158 +257,137 @@ export function AddAssembly({
|
|
|
153
257
|
handleClose()
|
|
154
258
|
event.preventDefault()
|
|
155
259
|
|
|
156
|
-
const { jobsManager } = session
|
|
157
|
-
const controller = new AbortController()
|
|
158
|
-
|
|
159
|
-
const job = {
|
|
160
|
-
name: `UploadAssemblyFile for ${assemblyName}`,
|
|
161
|
-
statusMessage: 'Pre-validating',
|
|
162
|
-
progressPct: 0,
|
|
163
|
-
cancelCallback: () => {
|
|
164
|
-
controller.abort()
|
|
165
|
-
jobsManager.abortJob(job.name)
|
|
166
|
-
},
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
jobsManager.runJob(job)
|
|
170
|
-
|
|
171
|
-
let fileId = ''
|
|
172
|
-
const { baseURL, getFetcher, internetAccountId } = selectedInternetAccount
|
|
173
|
-
if (fileType !== FileType.EXTERNAL && file) {
|
|
174
|
-
// First upload file
|
|
175
|
-
const url = new URL('files', baseURL)
|
|
176
|
-
url.searchParams.set('type', fileType)
|
|
177
|
-
const uri = url.href
|
|
178
|
-
const formData = new FormData()
|
|
179
|
-
formData.append('file', file)
|
|
180
|
-
formData.append('fileName', file.name)
|
|
181
|
-
formData.append('type', fileType)
|
|
182
|
-
const apolloFetchFile = getFetcher({
|
|
183
|
-
locationType: 'UriLocation',
|
|
184
|
-
uri,
|
|
185
|
-
})
|
|
186
|
-
if (apolloFetchFile) {
|
|
187
|
-
jobsManager.update(job.name, 'Uploading file, this may take awhile')
|
|
188
|
-
const { signal } = controller
|
|
189
|
-
const response = await apolloFetchFile(uri, {
|
|
190
|
-
method: 'POST',
|
|
191
|
-
body: formData,
|
|
192
|
-
signal,
|
|
193
|
-
})
|
|
194
|
-
if (!response.ok) {
|
|
195
|
-
const newErrorMessage = await createFetchErrorMessage(
|
|
196
|
-
response,
|
|
197
|
-
'Error when inserting new assembly (while uploading file)',
|
|
198
|
-
)
|
|
199
|
-
jobsManager.abortJob(job.name, newErrorMessage)
|
|
200
|
-
setErrorMessage(newErrorMessage)
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
const result = await response.json()
|
|
204
|
-
fileId = result._id
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
260
|
let change:
|
|
209
261
|
| AddAssemblyFromExternalChange
|
|
210
262
|
| AddAssemblyAndFeaturesFromFileChange
|
|
211
263
|
| AddAssemblyFromFileChange
|
|
264
|
+
|
|
212
265
|
if (fileType === FileType.EXTERNAL) {
|
|
213
266
|
change = new AddAssemblyFromExternalChange({
|
|
214
267
|
typeName: 'AddAssemblyFromExternalChange',
|
|
215
|
-
|
|
216
268
|
assembly: new ObjectID().toHexString(),
|
|
217
269
|
assemblyName,
|
|
218
270
|
externalLocation: {
|
|
219
|
-
fa:
|
|
220
|
-
fai:
|
|
221
|
-
|
|
271
|
+
fa: fastaUrl,
|
|
272
|
+
fai: fastaIndexUrl,
|
|
273
|
+
gzi: fastaGziIndexUrl,
|
|
222
274
|
},
|
|
223
275
|
})
|
|
224
276
|
} else {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
assemblyName,
|
|
228
|
-
fileIds: { fa: fileId },
|
|
277
|
+
if (!fastaFile) {
|
|
278
|
+
throw new Error('Missing fasta file')
|
|
229
279
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
280
|
+
if (fileType === FileType.GFF3 && importFeatures) {
|
|
281
|
+
const faId = await uploadFile(fastaFile, FileType.GFF3)
|
|
282
|
+
change = new AddAssemblyAndFeaturesFromFileChange({
|
|
283
|
+
typeName: 'AddAssemblyAndFeaturesFromFileChange',
|
|
284
|
+
assembly: new ObjectID().toHexString(),
|
|
285
|
+
assemblyName,
|
|
286
|
+
fileIds: { fa: faId },
|
|
287
|
+
})
|
|
288
|
+
} else if (fileType === FileType.GFF3) {
|
|
289
|
+
const faId = await uploadFile(fastaFile, FileType.GFF3)
|
|
290
|
+
change = new AddAssemblyFromFileChange({
|
|
291
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
292
|
+
assembly: new ObjectID().toHexString(),
|
|
293
|
+
assemblyName,
|
|
294
|
+
fileIds: {
|
|
295
|
+
fa: faId,
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
} else if (sequenceIsEditable) {
|
|
299
|
+
const faId = await uploadFile(fastaFile, FileType.FASTA)
|
|
300
|
+
change = new AddAssemblyFromFileChange({
|
|
301
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
302
|
+
assembly: new ObjectID().toHexString(),
|
|
303
|
+
assemblyName,
|
|
304
|
+
fileIds: {
|
|
305
|
+
fa: faId,
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
} else {
|
|
309
|
+
if (!fastaIndexFile || !fastaGziIndexFile) {
|
|
310
|
+
throw new Error('Missing fasta index files')
|
|
311
|
+
}
|
|
312
|
+
const faId = await uploadFile(fastaFile, FileType.BGZIP_FASTA)
|
|
313
|
+
const faiId = await uploadFile(fastaIndexFile, FileType.FAI)
|
|
314
|
+
const gziId = await uploadFile(fastaGziIndexFile, FileType.GZI)
|
|
241
315
|
|
|
242
|
-
|
|
316
|
+
change = new AddAssemblyFromFileChange({
|
|
317
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
318
|
+
assembly: new ObjectID().toHexString(),
|
|
319
|
+
assemblyName,
|
|
320
|
+
fileIds: {
|
|
321
|
+
fa: faId,
|
|
322
|
+
fai: faiId,
|
|
323
|
+
gzi: gziId,
|
|
324
|
+
},
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
}
|
|
243
328
|
|
|
329
|
+
const [{ internetAccountId }] = apolloInternetAccounts
|
|
244
330
|
await changeManager.submit(change, {
|
|
245
331
|
internetAccountId,
|
|
246
332
|
updateJobsManager: true,
|
|
247
333
|
})
|
|
248
|
-
|
|
249
334
|
setSubmitted(false)
|
|
250
335
|
setLoading(false)
|
|
251
336
|
}
|
|
252
337
|
|
|
253
|
-
let
|
|
338
|
+
let validFastaUrl = false
|
|
254
339
|
try {
|
|
255
|
-
const url = new URL(
|
|
340
|
+
const url = new URL(fastaUrl)
|
|
256
341
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
257
|
-
|
|
342
|
+
validFastaUrl = true
|
|
258
343
|
}
|
|
259
344
|
} catch {
|
|
260
345
|
// pass
|
|
261
346
|
}
|
|
262
|
-
let
|
|
347
|
+
let validFastaIndexUrl = false
|
|
263
348
|
try {
|
|
264
|
-
const url = new URL(
|
|
349
|
+
const url = new URL(fastaIndexUrl)
|
|
265
350
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
266
|
-
|
|
351
|
+
validFastaIndexUrl = true
|
|
267
352
|
}
|
|
268
353
|
} catch {
|
|
269
354
|
// pass
|
|
270
355
|
}
|
|
271
|
-
let
|
|
356
|
+
let validFastaGziIndexUrl = false
|
|
272
357
|
try {
|
|
273
|
-
const url = new URL(
|
|
358
|
+
const url = new URL(fastaGziIndexUrl)
|
|
274
359
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
275
|
-
|
|
360
|
+
validFastaGziIndexUrl = true
|
|
276
361
|
}
|
|
277
362
|
} catch {
|
|
278
363
|
// pass
|
|
279
364
|
}
|
|
280
365
|
|
|
366
|
+
const [expanded, setExpanded] = React.useState<string>('panelFastaInput')
|
|
367
|
+
|
|
368
|
+
const handleAccordionChange =
|
|
369
|
+
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
|
|
370
|
+
if (newExpanded) {
|
|
371
|
+
setExpanded(panel)
|
|
372
|
+
}
|
|
373
|
+
if (panel === 'panelGffInput') {
|
|
374
|
+
setIsGzip(false)
|
|
375
|
+
} else {
|
|
376
|
+
setIsGzip(true)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
281
380
|
return (
|
|
282
381
|
<Dialog
|
|
283
|
-
open
|
|
284
|
-
|
|
382
|
+
open={true}
|
|
383
|
+
handleClose={handleClose}
|
|
285
384
|
data-testid="add-assembly-dialog"
|
|
286
385
|
title="Add new assembly"
|
|
287
|
-
|
|
386
|
+
maxWidth={false}
|
|
288
387
|
>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
{apolloInternetAccounts.length > 1 ? (
|
|
293
|
-
<>
|
|
294
|
-
<DialogContentText>Select account</DialogContentText>
|
|
295
|
-
<Select
|
|
296
|
-
value={selectedInternetAccount.internetAccountId}
|
|
297
|
-
onChange={handleChangeInternetAccount}
|
|
298
|
-
disabled={submitted && !errorMessage}
|
|
299
|
-
>
|
|
300
|
-
{internetAccounts.map((option) => (
|
|
301
|
-
<MenuItem key={option.id} value={option.internetAccountId}>
|
|
302
|
-
{option.name}
|
|
303
|
-
</MenuItem>
|
|
304
|
-
))}
|
|
305
|
-
</Select>
|
|
306
|
-
</>
|
|
307
|
-
) : null}
|
|
388
|
+
<form onSubmit={onSubmit} data-testid="submit-form">
|
|
389
|
+
<DialogContent className={classes.dialog}>
|
|
390
|
+
{loading ? <LinearProgress /> : null}
|
|
308
391
|
<TextField
|
|
309
392
|
margin="dense"
|
|
310
393
|
id="name"
|
|
@@ -319,147 +402,384 @@ export function AddAssembly({
|
|
|
319
402
|
}}
|
|
320
403
|
disabled={submitted && !errorMessage}
|
|
321
404
|
/>
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
405
|
+
|
|
406
|
+
<Accordion
|
|
407
|
+
disableGutters
|
|
408
|
+
elevation={0}
|
|
409
|
+
square
|
|
410
|
+
className={classes.accordion}
|
|
411
|
+
expanded={expanded === 'panelFastaInput'}
|
|
412
|
+
onChange={handleAccordionChange('panelFastaInput')}
|
|
413
|
+
>
|
|
414
|
+
<AccordionSummary
|
|
415
|
+
className={classes.accordionSummary}
|
|
416
|
+
expandIcon={
|
|
417
|
+
expanded === 'panelFastaInput' ? (
|
|
418
|
+
<RadioButtonCheckedIcon
|
|
419
|
+
className={classes.radioIcon}
|
|
420
|
+
sx={{ fontSize: '1.2rem', ml: 5 }}
|
|
421
|
+
/>
|
|
422
|
+
) : (
|
|
423
|
+
<RadioButtonUncheckedIcon
|
|
424
|
+
className={classes.radioIcon}
|
|
425
|
+
sx={{ fontSize: '1.2rem', mr: 5 }}
|
|
426
|
+
/>
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
aria-controls="panelFastaInputd-content"
|
|
430
|
+
id="panelFastaInputd-header"
|
|
330
431
|
>
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
label="GFF3"
|
|
335
|
-
disabled={submitted && !errorMessage}
|
|
336
|
-
/>
|
|
337
|
-
<FormControlLabel
|
|
338
|
-
value={FileType.FASTA}
|
|
339
|
-
control={<Radio />}
|
|
340
|
-
label="FASTA"
|
|
341
|
-
disabled={submitted && !errorMessage}
|
|
342
|
-
/>
|
|
343
|
-
<FormControlLabel
|
|
344
|
-
value={FileType.EXTERNAL}
|
|
345
|
-
control={<Radio />}
|
|
346
|
-
label="External"
|
|
347
|
-
disabled={submitted && !errorMessage}
|
|
348
|
-
/>
|
|
349
|
-
</RadioGroup>
|
|
350
|
-
</FormControl>
|
|
351
|
-
{fileType === FileType.EXTERNAL ? (
|
|
352
|
-
<Box style={{ marginTop: 20 }}>
|
|
353
|
-
<Typography variant="caption">
|
|
354
|
-
Enter FASTA and FASTA index(es) URL
|
|
355
|
-
</Typography>
|
|
356
|
-
<TextField
|
|
357
|
-
margin="dense"
|
|
358
|
-
helperText="Can be bgz-compressed"
|
|
359
|
-
id="fasta"
|
|
360
|
-
label="FASTA"
|
|
361
|
-
type="TextField"
|
|
362
|
-
fullWidth
|
|
363
|
-
variant="outlined"
|
|
364
|
-
error={!validFastaFile}
|
|
365
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
366
|
-
setFastaFile(e.target.value)
|
|
367
|
-
}}
|
|
368
|
-
disabled={submitted && !errorMessage}
|
|
369
|
-
InputProps={{
|
|
370
|
-
startAdornment: (
|
|
371
|
-
<InputAdornment position="start">
|
|
372
|
-
<LinkIcon />
|
|
373
|
-
</InputAdornment>
|
|
374
|
-
),
|
|
375
|
-
}}
|
|
376
|
-
/>
|
|
377
|
-
<TextField
|
|
378
|
-
margin="dense"
|
|
379
|
-
id="fasta-index"
|
|
380
|
-
label="FASTA Index"
|
|
381
|
-
helperText=".fai or .gz.fai"
|
|
382
|
-
type="TextField"
|
|
383
|
-
fullWidth
|
|
384
|
-
variant="outlined"
|
|
385
|
-
error={!validFastaIndexFile}
|
|
386
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
387
|
-
setFastaIndexFile(e.target.value)
|
|
388
|
-
}}
|
|
389
|
-
disabled={submitted && !errorMessage}
|
|
390
|
-
InputProps={{
|
|
391
|
-
startAdornment: (
|
|
392
|
-
<InputAdornment position="start">
|
|
393
|
-
<LinkIcon />
|
|
394
|
-
</InputAdornment>
|
|
395
|
-
),
|
|
396
|
-
}}
|
|
397
|
-
/>
|
|
398
|
-
<TextField
|
|
399
|
-
margin="dense"
|
|
400
|
-
id="fasta-gzi-index"
|
|
401
|
-
label="FASTA GZI Index"
|
|
402
|
-
helperText="Only for bgz-compressed FASTA, .gz.gzi"
|
|
403
|
-
type="TextField"
|
|
404
|
-
fullWidth
|
|
405
|
-
variant="outlined"
|
|
406
|
-
error={Boolean(fastaGziIndexFile) && !validFastaGziIndexFile}
|
|
407
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
408
|
-
setFastaGziIndexFile(e.target.value)
|
|
409
|
-
}}
|
|
410
|
-
disabled={submitted && !errorMessage}
|
|
411
|
-
InputProps={{
|
|
412
|
-
startAdornment: (
|
|
413
|
-
<InputAdornment position="start">
|
|
414
|
-
<LinkIcon />
|
|
415
|
-
</InputAdornment>
|
|
416
|
-
),
|
|
417
|
-
}}
|
|
418
|
-
/>
|
|
419
|
-
</Box>
|
|
420
|
-
) : (
|
|
421
|
-
<Box style={{ marginTop: 20 }}>
|
|
422
|
-
<input
|
|
423
|
-
type="file"
|
|
424
|
-
onChange={handleChangeFile}
|
|
425
|
-
disabled={submitted && !errorMessage}
|
|
426
|
-
/>
|
|
432
|
+
<Typography component="span">FASTA input</Typography>
|
|
433
|
+
</AccordionSummary>
|
|
434
|
+
<AccordionDetails className={classes.accordionDetails}>
|
|
427
435
|
<FormGroup>
|
|
428
436
|
<FormControlLabel
|
|
437
|
+
data-testid="files-on-url-checkbox"
|
|
429
438
|
control={
|
|
430
439
|
<Checkbox
|
|
431
|
-
checked={fileType === FileType.GFF3 && importFeatures}
|
|
432
440
|
onChange={() => {
|
|
433
|
-
|
|
441
|
+
setFileType(
|
|
442
|
+
fileType === FileType.EXTERNAL
|
|
443
|
+
? FileType.BGZIP_FASTA
|
|
444
|
+
: FileType.EXTERNAL,
|
|
445
|
+
)
|
|
446
|
+
if (fileType === FileType.EXTERNAL) {
|
|
447
|
+
setSequenceIsEditable(false)
|
|
448
|
+
}
|
|
434
449
|
}}
|
|
450
|
+
checked={fileType === FileType.EXTERNAL}
|
|
435
451
|
disabled={
|
|
436
|
-
fileType !== FileType.GFF3
|
|
437
|
-
(submitted && !errorMessage)
|
|
452
|
+
sequenceIsEditable && fileType !== FileType.GFF3
|
|
438
453
|
}
|
|
439
454
|
/>
|
|
440
455
|
}
|
|
441
|
-
label=
|
|
456
|
+
label={
|
|
457
|
+
<Box display="flex" alignItems="center">
|
|
458
|
+
Use external URLs
|
|
459
|
+
<Tooltip
|
|
460
|
+
title="Use external URLs to provide FASTA and index files. Does not copy the files to the Apollo collaboration server, so ensure the URLs are stable."
|
|
461
|
+
placement="top-start"
|
|
462
|
+
>
|
|
463
|
+
<IconButton size="small">
|
|
464
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
465
|
+
</IconButton>
|
|
466
|
+
</Tooltip>
|
|
467
|
+
</Box>
|
|
468
|
+
}
|
|
442
469
|
/>
|
|
470
|
+
|
|
471
|
+
<FormControlLabel
|
|
472
|
+
data-testid="sequence-is-editable-checkbox"
|
|
473
|
+
control={
|
|
474
|
+
<Checkbox
|
|
475
|
+
onChange={() => {
|
|
476
|
+
setSequenceIsEditable(!sequenceIsEditable)
|
|
477
|
+
}}
|
|
478
|
+
/>
|
|
479
|
+
}
|
|
480
|
+
checked={sequenceIsEditable}
|
|
481
|
+
disabled={fileType === FileType.EXTERNAL}
|
|
482
|
+
label={
|
|
483
|
+
<Box display="flex" alignItems="center">
|
|
484
|
+
Store sequence in database
|
|
485
|
+
<Tooltip
|
|
486
|
+
title="Enables users to edit the genomic sequence, but comes with performance impacts. Use with care."
|
|
487
|
+
placement="top-start"
|
|
488
|
+
>
|
|
489
|
+
<IconButton size="small">
|
|
490
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
491
|
+
</IconButton>
|
|
492
|
+
</Tooltip>
|
|
493
|
+
</Box>
|
|
494
|
+
}
|
|
495
|
+
/>
|
|
496
|
+
<FormControlLabel
|
|
497
|
+
data-testid="fasta-is-gzip-checkbox"
|
|
498
|
+
control={
|
|
499
|
+
<Checkbox
|
|
500
|
+
checked={isGzip}
|
|
501
|
+
onChange={() => {
|
|
502
|
+
if (sequenceIsEditable) {
|
|
503
|
+
setIsGzip(!isGzip)
|
|
504
|
+
} else {
|
|
505
|
+
setIsGzip(true)
|
|
506
|
+
}
|
|
507
|
+
}}
|
|
508
|
+
disabled={!sequenceIsEditable}
|
|
509
|
+
/>
|
|
510
|
+
}
|
|
511
|
+
label="FASTA is gzip compressed"
|
|
512
|
+
/>
|
|
513
|
+
|
|
514
|
+
{fileType === FileType.BGZIP_FASTA ||
|
|
515
|
+
fileType === FileType.GFF3 ? (
|
|
516
|
+
<Table size="small" sx={{ mt: 2 }}>
|
|
517
|
+
<TableBody>
|
|
518
|
+
<TableRow>
|
|
519
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
520
|
+
<Box display="flex" alignItems="center">
|
|
521
|
+
<span>FASTA</span>
|
|
522
|
+
<Tooltip title='Unless "Store sequence in database" enabled, FASTA input must be compressed with bgzip and indexed with samtools faidx (or equivalent). Compression is optional for sequences stored in the database.'>
|
|
523
|
+
<IconButton size="small">
|
|
524
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
525
|
+
</IconButton>
|
|
526
|
+
</Tooltip>
|
|
527
|
+
</Box>
|
|
528
|
+
</TableCell>
|
|
529
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
530
|
+
<input
|
|
531
|
+
data-testid="fasta-input-file"
|
|
532
|
+
type="file"
|
|
533
|
+
onChange={(
|
|
534
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
535
|
+
) => {
|
|
536
|
+
setFastaFile(e.target.files?.item(0) ?? null)
|
|
537
|
+
}}
|
|
538
|
+
disabled={submitted && !errorMessage}
|
|
539
|
+
/>
|
|
540
|
+
</TableCell>
|
|
541
|
+
</TableRow>
|
|
542
|
+
|
|
543
|
+
<TableRow>
|
|
544
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
545
|
+
FASTA index (.fai)
|
|
546
|
+
</TableCell>
|
|
547
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
548
|
+
<input
|
|
549
|
+
data-testid="fai-input-file"
|
|
550
|
+
type="file"
|
|
551
|
+
onChange={(
|
|
552
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
553
|
+
) => {
|
|
554
|
+
setFastaIndexFile(e.target.files?.item(0) ?? null)
|
|
555
|
+
}}
|
|
556
|
+
disabled={
|
|
557
|
+
(submitted && !errorMessage) || sequenceIsEditable
|
|
558
|
+
}
|
|
559
|
+
/>
|
|
560
|
+
</TableCell>
|
|
561
|
+
</TableRow>
|
|
562
|
+
|
|
563
|
+
<TableRow>
|
|
564
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
565
|
+
FASTA binary index (.gzi)
|
|
566
|
+
</TableCell>
|
|
567
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
568
|
+
<input
|
|
569
|
+
data-testid="gzi-input-file"
|
|
570
|
+
type="file"
|
|
571
|
+
onChange={(
|
|
572
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
573
|
+
) => {
|
|
574
|
+
setFastaGziIndexFile(
|
|
575
|
+
e.target.files?.item(0) ?? null,
|
|
576
|
+
)
|
|
577
|
+
}}
|
|
578
|
+
disabled={
|
|
579
|
+
(submitted && !errorMessage) || sequenceIsEditable
|
|
580
|
+
}
|
|
581
|
+
/>
|
|
582
|
+
</TableCell>
|
|
583
|
+
</TableRow>
|
|
584
|
+
</TableBody>
|
|
585
|
+
</Table>
|
|
586
|
+
) : (
|
|
587
|
+
<Table size="small" sx={{ mt: 2 }}>
|
|
588
|
+
<TableBody>
|
|
589
|
+
<TableRow>
|
|
590
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
591
|
+
<Box display="flex" alignItems="center">
|
|
592
|
+
<span>FASTA</span>
|
|
593
|
+
<Tooltip title="Remote FASTA input must be compressed with bgzip and indexed with samtools faidx (or equivalent)">
|
|
594
|
+
<IconButton size="small">
|
|
595
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
596
|
+
</IconButton>
|
|
597
|
+
</Tooltip>
|
|
598
|
+
</Box>
|
|
599
|
+
</TableCell>
|
|
600
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
601
|
+
<TextField
|
|
602
|
+
data-testid="fasta-input-url"
|
|
603
|
+
variant="outlined"
|
|
604
|
+
value={fastaUrl}
|
|
605
|
+
error={!validFastaUrl}
|
|
606
|
+
onChange={(
|
|
607
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
608
|
+
) => {
|
|
609
|
+
setFastaUrl(e.target.value)
|
|
610
|
+
}}
|
|
611
|
+
disabled={submitted && !errorMessage}
|
|
612
|
+
slotProps={{
|
|
613
|
+
input: {
|
|
614
|
+
startAdornment: (
|
|
615
|
+
<InputAdornment position="start">
|
|
616
|
+
<LinkIcon />
|
|
617
|
+
</InputAdornment>
|
|
618
|
+
),
|
|
619
|
+
},
|
|
620
|
+
}}
|
|
621
|
+
/>
|
|
622
|
+
</TableCell>
|
|
623
|
+
</TableRow>
|
|
624
|
+
|
|
625
|
+
<TableRow>
|
|
626
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
627
|
+
FASTA index (.fai)
|
|
628
|
+
</TableCell>
|
|
629
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
630
|
+
<TextField
|
|
631
|
+
data-testid="fai-input-url"
|
|
632
|
+
variant="outlined"
|
|
633
|
+
value={fastaIndexUrl}
|
|
634
|
+
error={!validFastaIndexUrl}
|
|
635
|
+
onChange={(
|
|
636
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
637
|
+
) => {
|
|
638
|
+
setFastaIndexUrl(e.target.value)
|
|
639
|
+
}}
|
|
640
|
+
disabled={submitted && !errorMessage}
|
|
641
|
+
slotProps={{
|
|
642
|
+
input: {
|
|
643
|
+
startAdornment: (
|
|
644
|
+
<InputAdornment position="start">
|
|
645
|
+
<LinkIcon />
|
|
646
|
+
</InputAdornment>
|
|
647
|
+
),
|
|
648
|
+
},
|
|
649
|
+
}}
|
|
650
|
+
/>
|
|
651
|
+
</TableCell>
|
|
652
|
+
</TableRow>
|
|
653
|
+
|
|
654
|
+
<TableRow>
|
|
655
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
656
|
+
FASTA binary index (.gzi)
|
|
657
|
+
</TableCell>
|
|
658
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
659
|
+
<TextField
|
|
660
|
+
data-testid="gzi-input-url"
|
|
661
|
+
variant="outlined"
|
|
662
|
+
value={fastaGziIndexUrl}
|
|
663
|
+
error={!validFastaGziIndexUrl}
|
|
664
|
+
onChange={(
|
|
665
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
666
|
+
) => {
|
|
667
|
+
setFastaGziIndexUrl(e.target.value)
|
|
668
|
+
}}
|
|
669
|
+
disabled={submitted && !errorMessage}
|
|
670
|
+
slotProps={{
|
|
671
|
+
input: {
|
|
672
|
+
startAdornment: (
|
|
673
|
+
<InputAdornment position="start">
|
|
674
|
+
<LinkIcon />
|
|
675
|
+
</InputAdornment>
|
|
676
|
+
),
|
|
677
|
+
},
|
|
678
|
+
}}
|
|
679
|
+
/>
|
|
680
|
+
</TableCell>
|
|
681
|
+
</TableRow>
|
|
682
|
+
</TableBody>
|
|
683
|
+
</Table>
|
|
684
|
+
)}
|
|
443
685
|
</FormGroup>
|
|
444
|
-
</
|
|
445
|
-
|
|
686
|
+
</AccordionDetails>
|
|
687
|
+
</Accordion>
|
|
688
|
+
<Accordion
|
|
689
|
+
disableGutters
|
|
690
|
+
elevation={0}
|
|
691
|
+
square
|
|
692
|
+
className={classes.accordion}
|
|
693
|
+
expanded={expanded === 'panelGffInput'}
|
|
694
|
+
onChange={handleAccordionChange('panelGffInput')}
|
|
695
|
+
>
|
|
696
|
+
<AccordionSummary
|
|
697
|
+
className={classes.accordionSummary}
|
|
698
|
+
expandIcon={
|
|
699
|
+
expanded === 'panelGffInput' ? (
|
|
700
|
+
<RadioButtonCheckedIcon
|
|
701
|
+
className={classes.radioIcon}
|
|
702
|
+
sx={{ fontSize: '1.2rem', ml: 5 }}
|
|
703
|
+
/>
|
|
704
|
+
) : (
|
|
705
|
+
<RadioButtonUncheckedIcon
|
|
706
|
+
className={classes.radioIcon}
|
|
707
|
+
sx={{ fontSize: '1.2rem', mr: 5 }}
|
|
708
|
+
/>
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
aria-controls="panelGffInputd-content"
|
|
712
|
+
>
|
|
713
|
+
<Typography component="span">
|
|
714
|
+
GFF3 input
|
|
715
|
+
<Tooltip title="GFF3 must includes FASTA sequences. File can be gzip compressed.">
|
|
716
|
+
<InfoIcon
|
|
717
|
+
className={classes.radioIcon}
|
|
718
|
+
sx={{ fontSize: 18 }}
|
|
719
|
+
/>
|
|
720
|
+
</Tooltip>
|
|
721
|
+
</Typography>
|
|
722
|
+
</AccordionSummary>
|
|
723
|
+
<AccordionDetails className={classes.accordionDetails}>
|
|
724
|
+
<Box style={{ marginTop: 20 }}>
|
|
725
|
+
<input
|
|
726
|
+
data-testid="gff3-input-file"
|
|
727
|
+
type="file"
|
|
728
|
+
disabled={submitted && !errorMessage}
|
|
729
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
730
|
+
setFastaFile(e.target.files?.item(0) ?? null)
|
|
731
|
+
setFileType(FileType.GFF3)
|
|
732
|
+
}}
|
|
733
|
+
/>
|
|
734
|
+
<FormGroup style={{ display: 'grid' }}>
|
|
735
|
+
<FormControlLabel
|
|
736
|
+
control={
|
|
737
|
+
<Checkbox
|
|
738
|
+
checked={importFeatures}
|
|
739
|
+
onChange={() => {
|
|
740
|
+
setImportFeatures(!importFeatures)
|
|
741
|
+
}}
|
|
742
|
+
disabled={submitted && !errorMessage}
|
|
743
|
+
/>
|
|
744
|
+
}
|
|
745
|
+
label="Load features from GFF3 file"
|
|
746
|
+
/>
|
|
747
|
+
<FormControlLabel
|
|
748
|
+
data-testid="gff3-is-gzip-checkbox"
|
|
749
|
+
control={
|
|
750
|
+
<Checkbox
|
|
751
|
+
checked={isGzip}
|
|
752
|
+
onChange={() => {
|
|
753
|
+
setIsGzip(!isGzip)
|
|
754
|
+
}}
|
|
755
|
+
disabled={submitted && !errorMessage}
|
|
756
|
+
/>
|
|
757
|
+
}
|
|
758
|
+
label="GFF3 is gzip compressed"
|
|
759
|
+
/>
|
|
760
|
+
</FormGroup>
|
|
761
|
+
</Box>
|
|
762
|
+
</AccordionDetails>
|
|
763
|
+
</Accordion>
|
|
446
764
|
</DialogContent>
|
|
447
765
|
<DialogActions>
|
|
448
766
|
<Button
|
|
449
767
|
disabled={
|
|
450
|
-
!
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
768
|
+
!checkSumbission(
|
|
769
|
+
validAsm,
|
|
770
|
+
sequenceIsEditable,
|
|
771
|
+
fileType,
|
|
772
|
+
fastaFile,
|
|
773
|
+
fastaIndexFile,
|
|
774
|
+
fastaGziIndexFile,
|
|
775
|
+
validFastaUrl,
|
|
776
|
+
validFastaIndexUrl,
|
|
777
|
+
validFastaGziIndexUrl,
|
|
778
|
+
) || submitted
|
|
460
779
|
}
|
|
461
780
|
variant="contained"
|
|
462
781
|
type="submit"
|
|
782
|
+
data-testid="submit-button"
|
|
463
783
|
>
|
|
464
784
|
{submitted ? 'Submitting...' : 'Submit'}
|
|
465
785
|
</Button>
|