@apollo-annotation/jbrowse-plugin-apollo 0.3.6 → 0.3.8
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 +4603 -2045
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +4611 -2039
- 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 +9387 -4016
- 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 +15 -15
- package/src/ApolloInternetAccount/model.ts +48 -13
- package/src/BackendDrivers/CollaborationServerDriver.ts +23 -2
- package/src/ChangeManager.ts +42 -18
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
- package/src/FeatureDetailsWidget/Attributes.tsx +8 -3
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -81
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +946 -190
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +4 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +61 -73
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +55 -211
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +562 -108
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +78 -14
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +15 -9
- package/src/LinearApolloDisplay/stateModel/base.ts +63 -43
- package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +79 -292
- package/src/LinearApolloDisplay/stateModel/rendering.ts +45 -344
- package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
- package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
- package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
- package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +481 -0
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +102 -40
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +12 -20
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +382 -243
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +83 -4
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +23 -11
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +118 -123
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +53 -63
- package/src/OntologyManager/index.ts +4 -1
- package/src/TabularEditor/HybridGrid/Feature.tsx +20 -14
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +108 -16
- package/src/components/AddAssembly.tsx +1 -1
- package/src/components/AddAssemblyAliases.tsx +114 -0
- package/src/components/AddChildFeature.tsx +7 -7
- package/src/components/AddFeature.tsx +20 -15
- package/src/components/AddRefSeqAliases.tsx +9 -9
- package/src/components/CopyFeature.tsx +4 -4
- package/src/components/CreateApolloAnnotation.tsx +335 -151
- package/src/components/DeleteAssembly.tsx +1 -1
- package/src/components/DeleteFeature.tsx +358 -11
- package/src/components/DownloadGFF3.tsx +20 -1
- package/src/components/EditZoomThresholdDialog.tsx +69 -0
- package/src/components/FilterFeatures.tsx +7 -7
- package/src/components/FilterTranscripts.tsx +86 -0
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/ManageChecks.tsx +1 -1
- package/src/components/MergeExons.tsx +193 -0
- package/src/components/MergeTranscripts.tsx +182 -0
- package/src/components/OntologyTermMultiSelect.tsx +11 -11
- package/src/components/OpenLocalFile.tsx +11 -7
- package/src/components/SplitExon.tsx +134 -0
- package/src/components/ViewCheckResults.tsx +1 -1
- package/src/components/index.ts +4 -0
- package/src/config.ts +11 -0
- package/src/extensions/annotationFromJBrowseFeature.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +99 -89
- package/src/index.ts +42 -105
- package/src/makeDisplayComponent.tsx +0 -1
- package/src/menus/index.ts +1 -0
- package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +60 -33
- package/src/menus/topLevelMenuAdmin.ts +154 -0
- package/src/session/session.ts +163 -104
- package/src/util/annotationFeatureUtils.ts +59 -0
- package/src/util/copyToClipboard.ts +21 -0
- package/src/util/displayUtils.ts +149 -0
- package/src/util/glyphUtils.ts +201 -0
- package/src/util/index.ts +2 -0
- package/src/util/mouseEventsUtils.ts +145 -0
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
4
4
|
|
|
5
5
|
import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
AddFeatureChange,
|
|
8
|
+
LocationEndChange,
|
|
9
|
+
LocationStartChange,
|
|
10
|
+
} from '@apollo-annotation/shared'
|
|
7
11
|
import { type Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
8
12
|
import { type AbstractSessionModel } from '@jbrowse/core/util'
|
|
9
13
|
import {
|
|
@@ -15,11 +19,13 @@ import {
|
|
|
15
19
|
DialogContentText,
|
|
16
20
|
DialogTitle,
|
|
17
21
|
FormControlLabel,
|
|
22
|
+
FormGroup,
|
|
18
23
|
MenuItem,
|
|
19
24
|
Select,
|
|
20
25
|
type SelectChangeEvent,
|
|
21
26
|
Typography,
|
|
22
27
|
} from '@mui/material'
|
|
28
|
+
import ObjectID from 'bson-objectid'
|
|
23
29
|
import { getSnapshot } from 'mobx-state-tree'
|
|
24
30
|
import React, { useEffect, useMemo, useState } from 'react'
|
|
25
31
|
|
|
@@ -33,6 +39,10 @@ interface CreateApolloAnnotationProps {
|
|
|
33
39
|
annotationFeature: AnnotationFeatureSnapshot
|
|
34
40
|
assembly: Assembly
|
|
35
41
|
refSeqId: string
|
|
42
|
+
region: {
|
|
43
|
+
start: number
|
|
44
|
+
end: number
|
|
45
|
+
}
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
const isGeneOrTranscript = (
|
|
@@ -88,41 +98,59 @@ const isTranscript = (
|
|
|
88
98
|
)
|
|
89
99
|
}
|
|
90
100
|
|
|
91
|
-
|
|
101
|
+
export function getFeatureName(feature: AnnotationFeatureSnapshot) {
|
|
92
102
|
const { attributes } = feature
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
const keys = ['name', 'gff_name', 'transcript_name', 'gene_name']
|
|
104
|
+
for (const key of keys) {
|
|
105
|
+
const value = attributes?.[key]
|
|
106
|
+
if (value?.[0]) {
|
|
107
|
+
return value[0]
|
|
108
|
+
}
|
|
96
109
|
}
|
|
97
|
-
return
|
|
110
|
+
return ''
|
|
98
111
|
}
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
feature
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let attrName = ''
|
|
111
|
-
|
|
112
|
-
if (featureTypeOntology.isTypeOf(feature.type, 'gene')) {
|
|
113
|
-
attrName = 'gene_name'
|
|
113
|
+
export function getGeneNameOrId(feature: AnnotationFeatureSnapshot) {
|
|
114
|
+
const { attributes } = feature
|
|
115
|
+
const keys = ['gene_name', 'gene_id', 'gene_stable_id']
|
|
116
|
+
for (const key of keys) {
|
|
117
|
+
const value = attributes?.[key]
|
|
118
|
+
if (value?.[0]) {
|
|
119
|
+
return value[0]
|
|
120
|
+
}
|
|
114
121
|
}
|
|
122
|
+
return ''
|
|
123
|
+
}
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
export function getFeatureId(feature: AnnotationFeatureSnapshot) {
|
|
126
|
+
const { attributes } = feature
|
|
127
|
+
const keys = [
|
|
128
|
+
'id',
|
|
129
|
+
'gff_id',
|
|
130
|
+
'transcript_id',
|
|
131
|
+
'gene_id',
|
|
132
|
+
'gene_stable_id',
|
|
133
|
+
'stable_id',
|
|
134
|
+
]
|
|
135
|
+
for (const key of keys) {
|
|
136
|
+
const value = attributes?.[key]
|
|
137
|
+
if (value?.[0]) {
|
|
138
|
+
return value[0]
|
|
139
|
+
}
|
|
118
140
|
}
|
|
141
|
+
return ''
|
|
142
|
+
}
|
|
119
143
|
|
|
120
|
-
|
|
121
|
-
const name =
|
|
144
|
+
const getFeatureNameOrId = (feature: AnnotationFeatureSnapshot) => {
|
|
145
|
+
const name = getFeatureName(feature)
|
|
146
|
+
const id = getFeatureId(feature)
|
|
122
147
|
if (name) {
|
|
123
|
-
return name
|
|
148
|
+
return `${feature.type} - ${name}`
|
|
124
149
|
}
|
|
125
|
-
|
|
150
|
+
if (id) {
|
|
151
|
+
return `${feature.type} - ${id}`
|
|
152
|
+
}
|
|
153
|
+
return feature.type
|
|
126
154
|
}
|
|
127
155
|
|
|
128
156
|
export function CreateApolloAnnotation({
|
|
@@ -131,44 +159,46 @@ export function CreateApolloAnnotation({
|
|
|
131
159
|
handleClose,
|
|
132
160
|
refSeqId,
|
|
133
161
|
session,
|
|
162
|
+
region,
|
|
134
163
|
}: CreateApolloAnnotationProps) {
|
|
135
164
|
const apolloSessionModel = session as unknown as ApolloSessionModel
|
|
165
|
+
const { featureTypeOntology } =
|
|
166
|
+
apolloSessionModel.apolloDataStore.ontologyManager
|
|
136
167
|
const childIds = useMemo(
|
|
137
168
|
() => Object.keys(annotationFeature.children ?? {}),
|
|
138
169
|
[annotationFeature],
|
|
139
170
|
)
|
|
140
171
|
|
|
141
|
-
const features = useMemo(() => {
|
|
142
|
-
for (const [, asm] of apolloSessionModel.apolloDataStore.assemblies) {
|
|
143
|
-
if (asm._id === assembly.name) {
|
|
144
|
-
for (const [, refSeq] of asm.refSeqs) {
|
|
145
|
-
if (refSeq._id === refSeqId) {
|
|
146
|
-
return refSeq.features
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return []
|
|
152
|
-
}, [])
|
|
153
|
-
|
|
154
172
|
const [parentFeatureChecked, setParentFeatureChecked] = useState(true)
|
|
155
173
|
const [checkedChildrens, setCheckedChildrens] = useState<string[]>(childIds)
|
|
156
174
|
const [errorMessage, setErrorMessage] = useState('')
|
|
157
175
|
const [destinationFeatures, setDestinationFeatures] = useState<
|
|
158
176
|
AnnotationFeatureSnapshot[]
|
|
159
177
|
>([])
|
|
178
|
+
const [createNewGene, setCreateNewGene] = useState(false)
|
|
160
179
|
const [selectedDestinationFeature, setSelectedDestinationFeature] =
|
|
161
180
|
useState<AnnotationFeatureSnapshot>()
|
|
162
181
|
|
|
163
|
-
const
|
|
182
|
+
const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(
|
|
183
|
+
assembly.name,
|
|
184
|
+
)
|
|
185
|
+
const refSeq = apolloAssembly?.refSeqs.get(refSeqId)
|
|
186
|
+
const features = refSeq?.getFeatures(region.start, region.end)
|
|
187
|
+
|
|
188
|
+
const getDestinationFeatures = () => {
|
|
164
189
|
const filteredFeatures: AnnotationFeatureSnapshot[] = []
|
|
165
190
|
|
|
166
|
-
for (const
|
|
167
|
-
if (f.
|
|
191
|
+
for (const f of features ?? []) {
|
|
192
|
+
if (f.min > region.end || f.max < region.start) {
|
|
168
193
|
continue
|
|
169
194
|
}
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
|
|
196
|
+
// Destination feature should be of type gene amd should be on the same strand as the source feature
|
|
197
|
+
if (
|
|
198
|
+
featureTypeOntology?.isTypeOf(f.type, 'gene') &&
|
|
199
|
+
f.strand === annotationFeature.strand
|
|
200
|
+
) {
|
|
201
|
+
const featureSnapshot = getSnapshot(f)
|
|
172
202
|
filteredFeatures.push(featureSnapshot)
|
|
173
203
|
}
|
|
174
204
|
}
|
|
@@ -178,34 +208,10 @@ export function CreateApolloAnnotation({
|
|
|
178
208
|
|
|
179
209
|
useEffect(() => {
|
|
180
210
|
setErrorMessage('')
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
annotationFeature.children,
|
|
186
|
-
)
|
|
187
|
-
.filter((child) => isTranscript(child, apolloSessionModel))
|
|
188
|
-
.filter((child) => checkedChildrens.includes(child._id))
|
|
189
|
-
mins = checkedAnnotationFeatureChildren.map((f) => f.min)
|
|
190
|
-
maxes = checkedAnnotationFeatureChildren.map((f) => f.max)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const { featureTypeOntology } =
|
|
194
|
-
apolloSessionModel.apolloDataStore.ontologyManager
|
|
195
|
-
if (
|
|
196
|
-
featureTypeOntology &&
|
|
197
|
-
featureTypeOntology.isTypeOf(annotationFeature.type, 'transcript')
|
|
198
|
-
) {
|
|
199
|
-
mins = [annotationFeature.min, ...mins]
|
|
200
|
-
maxes = [annotationFeature.max, ...maxes]
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const min = Math.min(...mins)
|
|
204
|
-
const max = Math.max(...maxes)
|
|
205
|
-
const filteredFeatures = getFeatures(min, max)
|
|
206
|
-
setDestinationFeatures(filteredFeatures)
|
|
207
|
-
setSelectedDestinationFeature(filteredFeatures[0])
|
|
208
|
-
}, [checkedChildrens, parentFeatureChecked])
|
|
211
|
+
const features = getDestinationFeatures()
|
|
212
|
+
setDestinationFeatures(features)
|
|
213
|
+
setSelectedDestinationFeature(features[0])
|
|
214
|
+
}, [checkedChildrens, parentFeatureChecked, region])
|
|
209
215
|
|
|
210
216
|
const handleParentFeatureCheck = (
|
|
211
217
|
event: React.ChangeEvent<HTMLInputElement>,
|
|
@@ -235,82 +241,237 @@ export function CreateApolloAnnotation({
|
|
|
235
241
|
|
|
236
242
|
const handleCreateApolloAnnotation = async () => {
|
|
237
243
|
if (parentFeatureChecked) {
|
|
238
|
-
|
|
244
|
+
// IF SOURCE FEATURE IS GENE
|
|
239
245
|
if (isGene(annotationFeature, apolloSessionModel)) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
)
|
|
245
|
-
const childrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
246
|
-
for (const childId of checkedChildrens) {
|
|
247
|
-
childrens[childId] = annotationFeature.children[childId]
|
|
248
|
-
}
|
|
249
|
-
change = new AddFeatureChange({
|
|
250
|
-
changedIds: [annotationFeature._id],
|
|
251
|
-
typeName: 'AddFeatureChange',
|
|
252
|
-
assembly: assembly.name,
|
|
253
|
-
addedFeature: {
|
|
254
|
-
...annotationFeature,
|
|
255
|
-
children: childrens,
|
|
256
|
-
},
|
|
257
|
-
})
|
|
258
|
-
} else {
|
|
259
|
-
change = new AddFeatureChange({
|
|
260
|
-
changedIds: [annotationFeature._id],
|
|
261
|
-
typeName: 'AddFeatureChange',
|
|
262
|
-
assembly: assembly.name,
|
|
263
|
-
addedFeature: annotationFeature,
|
|
264
|
-
})
|
|
265
|
-
}
|
|
246
|
+
await copyGeneFeature()
|
|
247
|
+
session.notify(
|
|
248
|
+
'Successfully copied selected gene and transcript(s)',
|
|
249
|
+
'success',
|
|
250
|
+
)
|
|
266
251
|
}
|
|
267
|
-
|
|
268
252
|
if (isTranscript(annotationFeature, apolloSessionModel)) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
253
|
+
// IF THE SOURCE IS TRANSCRIPT AND THE DESTINATION IS SELECTED AND CREATE NEW GENE IS NOT CHECKED
|
|
254
|
+
if (selectedDestinationFeature && !createNewGene) {
|
|
255
|
+
const transcripts: Record<string, AnnotationFeatureSnapshot> = {}
|
|
256
|
+
transcripts[annotationFeature._id] = annotationFeature
|
|
257
|
+
|
|
258
|
+
// If source trancript doesn't overlap with destination gene
|
|
259
|
+
// If not overlapping, then extend the destination gene to include the transcript
|
|
260
|
+
if (
|
|
261
|
+
selectedDestinationFeature.max < annotationFeature.max ||
|
|
262
|
+
selectedDestinationFeature.min > annotationFeature.min
|
|
263
|
+
) {
|
|
264
|
+
const newMin = Math.min(
|
|
265
|
+
selectedDestinationFeature.min,
|
|
266
|
+
annotationFeature.min,
|
|
267
|
+
)
|
|
268
|
+
const newMax = Math.max(
|
|
269
|
+
selectedDestinationFeature.max,
|
|
270
|
+
annotationFeature.max,
|
|
271
|
+
)
|
|
272
|
+
await extendSelectedDestinationFeatureLocation(newMin, newMax)
|
|
273
|
+
await copyTranscriptsToDestinationGene(transcripts)
|
|
274
|
+
} else {
|
|
275
|
+
await copyTranscriptsToDestinationGene(transcripts)
|
|
276
|
+
}
|
|
277
|
+
session.notify(
|
|
278
|
+
'Successfully copied selected transcripts to destination gene',
|
|
279
|
+
'success',
|
|
280
|
+
)
|
|
277
281
|
} else {
|
|
278
|
-
|
|
279
|
-
|
|
282
|
+
// IF THERE IS NO DESTINATION GENE SELECTED AND CREATE NEW GENE IS CHECKED
|
|
283
|
+
const childrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
284
|
+
childrens[annotationFeature._id] = annotationFeature
|
|
285
|
+
await createNewGeneFeatureWithTranscripts(childrens)
|
|
286
|
+
session.notify(
|
|
287
|
+
'Successfully created a new gene with selected transcripts',
|
|
288
|
+
'success',
|
|
289
|
+
)
|
|
280
290
|
}
|
|
281
291
|
}
|
|
282
|
-
|
|
283
|
-
if (!change) {
|
|
284
|
-
return
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
await apolloSessionModel.apolloDataStore.changeManager.submit(change)
|
|
288
|
-
session.notify('Annotation added successfully', 'success')
|
|
289
|
-
handleClose()
|
|
290
292
|
} else {
|
|
293
|
+
// IF PARENT (GENE) FEATURE IS NOT CHECKED AND WE ARE COPYING CHILDREN (TRANSCRIPTS)
|
|
291
294
|
if (!annotationFeature.children) {
|
|
292
295
|
return
|
|
293
296
|
}
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
|
|
298
|
+
// IF DESTINATION IS SELECTED AND CREATE NEW GENE IS NOT CHECKED
|
|
299
|
+
if (selectedDestinationFeature && !createNewGene) {
|
|
300
|
+
const childrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
301
|
+
for (const childId of checkedChildrens) {
|
|
302
|
+
childrens[childId] = annotationFeature.children[childId]
|
|
303
|
+
}
|
|
304
|
+
const min = Math.min(
|
|
305
|
+
...Object.values(childrens).map((child) => child.min),
|
|
306
|
+
)
|
|
307
|
+
const max = Math.max(
|
|
308
|
+
...Object.values(childrens).map((child) => child.max),
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
// If source trancript doesn't overlap with destination gene
|
|
312
|
+
// If not overlapping, then extend the destination gene to include the transcript
|
|
313
|
+
if (
|
|
314
|
+
selectedDestinationFeature.min > min ||
|
|
315
|
+
selectedDestinationFeature.max < max
|
|
316
|
+
) {
|
|
317
|
+
const newMin = Math.min(selectedDestinationFeature.min, min)
|
|
318
|
+
const newMax = Math.max(selectedDestinationFeature.max, max)
|
|
319
|
+
await extendSelectedDestinationFeatureLocation(newMin, newMax)
|
|
320
|
+
await copyTranscriptsToDestinationGene(childrens)
|
|
321
|
+
} else {
|
|
322
|
+
await copyTranscriptsToDestinationGene(childrens)
|
|
323
|
+
}
|
|
324
|
+
session.notify(
|
|
325
|
+
'Successfully copied transcript to destination gene',
|
|
326
|
+
'success',
|
|
327
|
+
)
|
|
328
|
+
} else {
|
|
329
|
+
// IF THERE IS NO DESTINATION GENE SELECTED AND CREATE NEW GENE IS CHECKED
|
|
330
|
+
const childrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
331
|
+
for (const childId of checkedChildrens) {
|
|
332
|
+
childrens[childId] = annotationFeature.children[childId]
|
|
333
|
+
}
|
|
334
|
+
await createNewGeneFeatureWithTranscripts(childrens)
|
|
335
|
+
session.notify(
|
|
336
|
+
'Successfully created a new gene with selected transcript',
|
|
337
|
+
'success',
|
|
338
|
+
)
|
|
296
339
|
}
|
|
340
|
+
}
|
|
341
|
+
handleClose()
|
|
342
|
+
}
|
|
297
343
|
|
|
344
|
+
// Copies gene feature along with its selected children
|
|
345
|
+
const copyGeneFeature = async () => {
|
|
346
|
+
let change
|
|
347
|
+
if (
|
|
348
|
+
annotationFeature.children &&
|
|
349
|
+
checkedChildrens.length !==
|
|
350
|
+
Object.values(annotationFeature.children).length
|
|
351
|
+
) {
|
|
352
|
+
// IF SOME CHILDREN ARE CHECKED
|
|
353
|
+
const childrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
298
354
|
for (const childId of checkedChildrens) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
355
|
+
childrens[childId] = annotationFeature.children[childId]
|
|
356
|
+
}
|
|
357
|
+
change = new AddFeatureChange({
|
|
358
|
+
changedIds: [annotationFeature._id],
|
|
359
|
+
typeName: 'AddFeatureChange',
|
|
360
|
+
assembly: assembly.name,
|
|
361
|
+
addedFeature: {
|
|
362
|
+
...annotationFeature,
|
|
363
|
+
children: childrens,
|
|
364
|
+
},
|
|
365
|
+
})
|
|
366
|
+
} else {
|
|
367
|
+
// IF PARENT AND ALL CHILDREN ARE CHECKED
|
|
368
|
+
change = new AddFeatureChange({
|
|
369
|
+
changedIds: [annotationFeature._id],
|
|
370
|
+
typeName: 'AddFeatureChange',
|
|
371
|
+
assembly: assembly.name,
|
|
372
|
+
addedFeature: annotationFeature,
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
await submitChange(change)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const copyTranscriptsToDestinationGene = async (
|
|
380
|
+
transcripts: Record<string, AnnotationFeatureSnapshot>,
|
|
381
|
+
) => {
|
|
382
|
+
if (!selectedDestinationFeature) {
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
for (const transcriptId of Object.keys(transcripts)) {
|
|
386
|
+
const transcript = transcripts[transcriptId]
|
|
387
|
+
const change = new AddFeatureChange({
|
|
388
|
+
parentFeatureId: selectedDestinationFeature._id,
|
|
389
|
+
changedIds: [selectedDestinationFeature._id],
|
|
390
|
+
typeName: 'AddFeatureChange',
|
|
391
|
+
assembly: assembly.name,
|
|
392
|
+
addedFeature: transcript,
|
|
393
|
+
})
|
|
394
|
+
await submitChange(change)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const createNewGeneFeatureWithTranscripts = async (
|
|
399
|
+
childrens: Record<string, AnnotationFeatureSnapshot>,
|
|
400
|
+
) => {
|
|
401
|
+
const newGeneId = new ObjectID().toHexString()
|
|
402
|
+
const min = Math.min(...Object.values(childrens).map((child) => child.min))
|
|
403
|
+
const max = Math.max(...Object.values(childrens).map((child) => child.max))
|
|
404
|
+
const change = new AddFeatureChange({
|
|
405
|
+
changedIds: [newGeneId],
|
|
406
|
+
typeName: 'AddFeatureChange',
|
|
407
|
+
assembly: assembly.name,
|
|
408
|
+
addedFeature: {
|
|
409
|
+
_id: newGeneId,
|
|
410
|
+
refSeq: refSeqId,
|
|
411
|
+
min,
|
|
412
|
+
max,
|
|
413
|
+
strand: annotationFeature.strand,
|
|
414
|
+
type: 'gene',
|
|
415
|
+
children: childrens,
|
|
416
|
+
attributes: {
|
|
417
|
+
name: [getGeneNameOrId(annotationFeature)],
|
|
418
|
+
gene_name: [getGeneNameOrId(annotationFeature)],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
await submitChange(change)
|
|
423
|
+
apolloSessionModel.apolloSetSelectedFeature(newGeneId)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const extendSelectedDestinationFeatureLocation = async (
|
|
427
|
+
newMin: number,
|
|
428
|
+
newMax: number,
|
|
429
|
+
) => {
|
|
430
|
+
if (!selectedDestinationFeature) {
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
const changes = []
|
|
434
|
+
if (newMin !== selectedDestinationFeature.min) {
|
|
435
|
+
changes.push(
|
|
436
|
+
new LocationStartChange({
|
|
437
|
+
typeName: 'LocationStartChange',
|
|
302
438
|
changedIds: [selectedDestinationFeature._id],
|
|
303
|
-
|
|
439
|
+
featureId: selectedDestinationFeature._id,
|
|
304
440
|
assembly: assembly.name,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
441
|
+
oldStart: selectedDestinationFeature.min,
|
|
442
|
+
newStart: newMin,
|
|
443
|
+
}),
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
if (newMax !== selectedDestinationFeature.max) {
|
|
447
|
+
changes.push(
|
|
448
|
+
new LocationEndChange({
|
|
449
|
+
typeName: 'LocationEndChange',
|
|
450
|
+
changedIds: [selectedDestinationFeature._id],
|
|
451
|
+
featureId: selectedDestinationFeature._id,
|
|
452
|
+
assembly: assembly.name,
|
|
453
|
+
oldEnd: selectedDestinationFeature.max,
|
|
454
|
+
newEnd: newMax,
|
|
455
|
+
}),
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
for (const change of changes) {
|
|
459
|
+
await submitChange(change)
|
|
311
460
|
}
|
|
312
461
|
}
|
|
313
462
|
|
|
463
|
+
const submitChange = async (
|
|
464
|
+
change: AddFeatureChange | LocationStartChange | LocationEndChange,
|
|
465
|
+
) => {
|
|
466
|
+
await apolloSessionModel.apolloDataStore.changeManager.submit(change)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const handleCreateNewGeneChange = (
|
|
470
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
471
|
+
) => {
|
|
472
|
+
setCreateNewGene(e.target.checked)
|
|
473
|
+
}
|
|
474
|
+
|
|
314
475
|
return (
|
|
315
476
|
<Dialog
|
|
316
477
|
open
|
|
@@ -333,7 +494,7 @@ export function CreateApolloAnnotation({
|
|
|
333
494
|
onChange={handleParentFeatureCheck}
|
|
334
495
|
/>
|
|
335
496
|
}
|
|
336
|
-
label={`${getFeatureNameOrId(annotationFeature
|
|
497
|
+
label={`${getFeatureNameOrId(annotationFeature)} (${annotationFeature.min + 1}..${annotationFeature.max})`}
|
|
337
498
|
/>
|
|
338
499
|
)}
|
|
339
500
|
{annotationFeature.children && (
|
|
@@ -352,7 +513,7 @@ export function CreateApolloAnnotation({
|
|
|
352
513
|
}}
|
|
353
514
|
/>
|
|
354
515
|
}
|
|
355
|
-
label={`${getFeatureNameOrId(child
|
|
516
|
+
label={`${getFeatureNameOrId(child)} (${child.min + 1}..${child.max})`}
|
|
356
517
|
/>
|
|
357
518
|
))}
|
|
358
519
|
</Box>
|
|
@@ -362,26 +523,49 @@ export function CreateApolloAnnotation({
|
|
|
362
523
|
((!parentFeatureChecked && checkedChildrens.length > 0) ||
|
|
363
524
|
(parentFeatureChecked &&
|
|
364
525
|
isTranscript(annotationFeature, apolloSessionModel))) && (
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
>
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
526
|
+
<div
|
|
527
|
+
style={{
|
|
528
|
+
border: '1px solid #ccc',
|
|
529
|
+
marginTop: 20,
|
|
530
|
+
padding: 10,
|
|
531
|
+
borderRadius: 5,
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
<Box sx={{ ml: 3 }}>
|
|
535
|
+
<Typography variant="caption" fontSize={12}>
|
|
536
|
+
Select the destination feature to copy the selected features
|
|
537
|
+
</Typography>
|
|
538
|
+
|
|
539
|
+
<Box sx={{ mt: 1 }}>
|
|
540
|
+
<Select
|
|
541
|
+
labelId="label"
|
|
542
|
+
style={{ width: '100%' }}
|
|
543
|
+
value={selectedDestinationFeature?._id ?? ''}
|
|
544
|
+
onChange={handleDestinationFeatureChange}
|
|
545
|
+
disabled={createNewGene}
|
|
546
|
+
>
|
|
547
|
+
{destinationFeatures.map((f) => (
|
|
548
|
+
<MenuItem key={f._id} value={f._id}>
|
|
549
|
+
{`${getFeatureNameOrId(f)} (${f.min + 1}..${f.max})`}
|
|
550
|
+
</MenuItem>
|
|
551
|
+
))}
|
|
552
|
+
</Select>
|
|
553
|
+
</Box>
|
|
383
554
|
</Box>
|
|
384
|
-
|
|
555
|
+
<Box sx={{ ml: 3 }}>
|
|
556
|
+
<FormGroup>
|
|
557
|
+
<FormControlLabel
|
|
558
|
+
control={
|
|
559
|
+
<Checkbox
|
|
560
|
+
checked={createNewGene}
|
|
561
|
+
onChange={handleCreateNewGeneChange}
|
|
562
|
+
/>
|
|
563
|
+
}
|
|
564
|
+
label="Create new gene"
|
|
565
|
+
/>
|
|
566
|
+
</FormGroup>
|
|
567
|
+
</Box>
|
|
568
|
+
</div>
|
|
385
569
|
)}
|
|
386
570
|
</DialogContent>
|
|
387
571
|
<DialogActions>
|