@apollo-annotation/jbrowse-plugin-apollo 1.0.0 → 1.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/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
- package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
- package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
- package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
- package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
- package/dist/ApolloInternetAccount/model.d.ts +174 -0
- package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
- package/dist/ApolloJobModel.d.ts.map +1 -1
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
- package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
- package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
- package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
- package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
- package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
- package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
- package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
- package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
- package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
- package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
- package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
- package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
- package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
- package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
- package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
- package/dist/OntologyManager/index.d.ts +734 -0
- package/dist/OntologyManager/index.d.ts.map +1 -0
- package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
- package/dist/components/AddCodingSequence.d.ts +14 -0
- package/dist/components/AddCodingSequence.d.ts.map +1 -0
- package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
- package/dist/components/CopyFeature.d.ts.map +1 -1
- package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
- package/dist/components/DeleteFeature.d.ts.map +1 -1
- package/dist/components/DownloadGFF3.d.ts.map +1 -1
- package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
- package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
- package/dist/components/ImportAnnotations.d.ts +9 -0
- package/dist/components/ImportAnnotations.d.ts.map +1 -0
- package/dist/components/ManageUsers.d.ts.map +1 -1
- package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
- package/dist/components/ViewChangeLog.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +830 -434
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +825 -429
- 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 +863 -468
- 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/dist/session/ClientDataStore.d.ts +2194 -0
- package/dist/session/ClientDataStore.d.ts.map +1 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
- package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
- package/dist/session/session.d.ts +6788 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/util/displayUtils.d.ts +36 -0
- package/dist/util/displayUtils.d.ts.map +1 -0
- package/dist/util/sequenceUtils.d.ts +2 -0
- package/dist/util/sequenceUtils.d.ts.map +1 -0
- package/package.json +14 -14
- package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
- package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
- package/src/ApolloInternetAccount/model.ts +8 -5
- package/src/ApolloJobModel.ts +3 -8
- package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
- package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
- package/src/BackendDrivers/LocalDriver/LocalDriver.ts +13 -4
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
- package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
- package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
- package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
- package/src/FeatureDetailsWidget/Translation.tsx +358 -0
- package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
- package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
- package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
- package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
- package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
- package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
- package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
- package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
- package/src/OntologyManager/OntologyStore/index.ts +1 -1
- package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddCodingSequence.tsx +255 -0
- package/src/components/AddFeature.tsx +1 -1
- package/src/components/AddRefSeqAliases.tsx +3 -2
- package/src/components/CopyFeature.tsx +9 -7
- package/src/components/CreateApolloAnnotation.tsx +28 -23
- package/src/components/DeleteAssembly.tsx +2 -2
- package/src/components/DeleteFeature.tsx +1 -4
- package/src/components/DownloadGFF3.tsx +4 -3
- package/src/components/DuplicateFeatureDialog.tsx +154 -0
- package/src/components/DuplicateTranscript.tsx +1 -1
- package/src/components/FilterTranscripts.tsx +1 -1
- package/src/components/ImportAnnotations.tsx +108 -0
- package/src/components/ManageUsers.tsx +1 -2
- package/src/components/OntologyTermAutocomplete.tsx +1 -3
- package/src/components/OntologyTermMultiSelect.tsx +3 -1
- package/src/components/ViewChangeLog.tsx +2 -1
- package/src/components/index.ts +2 -0
- package/src/extensions/annotationFromPileup.ts +34 -26
- package/src/index.ts +9 -8
- package/src/session/ClientDataStore.ts +16 -21
- package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
- package/src/session/session.ts +49 -9
- package/src/util/annotationFeatureUtils.ts +1 -1
- package/src/util/displayUtils.ts +1 -1
- package/src/util/glyphUtils.ts +3 -3
- package/src/util/sequenceUtils.ts +29 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
|
+
import type {
|
|
3
|
+
AnnotationFeature,
|
|
4
|
+
AnnotationFeatureSnapshot,
|
|
5
|
+
} from '@apollo-annotation/mst'
|
|
6
|
+
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
7
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util'
|
|
8
|
+
import { revcom } from '@jbrowse/core/util'
|
|
9
|
+
import {
|
|
10
|
+
Button,
|
|
11
|
+
DialogActions,
|
|
12
|
+
DialogContent,
|
|
13
|
+
DialogContentText,
|
|
14
|
+
FormControl,
|
|
15
|
+
FormControlLabel,
|
|
16
|
+
Radio,
|
|
17
|
+
RadioGroup,
|
|
18
|
+
TextField,
|
|
19
|
+
} from '@mui/material'
|
|
20
|
+
import ObjectID from 'bson-objectid'
|
|
21
|
+
import React, { useState } from 'react'
|
|
22
|
+
|
|
23
|
+
import type { ChangeManager } from '../ChangeManager'
|
|
24
|
+
import type { ApolloSessionModel } from '../session'
|
|
25
|
+
import { findLongestOrf } from '../util/sequenceUtils'
|
|
26
|
+
|
|
27
|
+
import { Dialog } from './Dialog'
|
|
28
|
+
|
|
29
|
+
interface AddCodingSequenceProps {
|
|
30
|
+
session: ApolloSessionModel
|
|
31
|
+
handleClose(): void
|
|
32
|
+
sourceFeature: AnnotationFeature
|
|
33
|
+
sourceAssemblyId: string
|
|
34
|
+
changeManager: ChangeManager
|
|
35
|
+
refName: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Returns the genomic half-open boundary for stitched position `pos`.
|
|
39
|
+
// For strand +1: the inclusive start (min) of the nucleotide.
|
|
40
|
+
// For strand -1: the exclusive end (max) of the nucleotide.
|
|
41
|
+
// Symmetric usage:
|
|
42
|
+
// plus: cdsMin = helper(orf[0]), cdsMax = helper(orf[1]+3)
|
|
43
|
+
// minus: cdsMax = helper(orf[0]), cdsMin = helper(orf[1]+3)
|
|
44
|
+
function stitchedToGenomicBound(
|
|
45
|
+
pos: number,
|
|
46
|
+
exons: { min: number; max: number }[],
|
|
47
|
+
strand: 1 | -1,
|
|
48
|
+
): number {
|
|
49
|
+
let cum = 0
|
|
50
|
+
for (const exon of exons) {
|
|
51
|
+
const len = exon.max - exon.min
|
|
52
|
+
if (pos <= cum + len) {
|
|
53
|
+
const off = pos - cum
|
|
54
|
+
return strand === 1 ? exon.min + off : exon.max - off
|
|
55
|
+
}
|
|
56
|
+
cum += len
|
|
57
|
+
}
|
|
58
|
+
const last = exons.at(-1)
|
|
59
|
+
if (!last) {
|
|
60
|
+
throw new Error('No exons found')
|
|
61
|
+
}
|
|
62
|
+
return strand === 1 ? last.max : last.min
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function AddCodingSequence({
|
|
66
|
+
changeManager,
|
|
67
|
+
handleClose,
|
|
68
|
+
refName,
|
|
69
|
+
session,
|
|
70
|
+
sourceAssemblyId,
|
|
71
|
+
sourceFeature,
|
|
72
|
+
}: AddCodingSequenceProps) {
|
|
73
|
+
const [method, setMethod] = useState<'longest-orf' | 'manual'>('longest-orf')
|
|
74
|
+
const [minInput, setMinInput] = useState('')
|
|
75
|
+
const [maxInput, setMaxInput] = useState('')
|
|
76
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
77
|
+
|
|
78
|
+
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
79
|
+
event.preventDefault()
|
|
80
|
+
setErrorMessage('')
|
|
81
|
+
|
|
82
|
+
let cdsMin: number
|
|
83
|
+
let cdsMax: number
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
if (method === 'longest-orf') {
|
|
87
|
+
const exonParts = sourceFeature.transcriptExonParts.filter(
|
|
88
|
+
(p) => p.type === 'exon',
|
|
89
|
+
)
|
|
90
|
+
if (exonParts.length === 0) {
|
|
91
|
+
setErrorMessage('No exons found in this transcript')
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const backendDriver =
|
|
96
|
+
session.apolloDataStore.getBackendDriver(sourceAssemblyId)
|
|
97
|
+
if (!backendDriver) {
|
|
98
|
+
setErrorMessage('No backend driver found for this assembly')
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let stitchedSequence = ''
|
|
103
|
+
for (const exon of exonParts) {
|
|
104
|
+
const { seq } = await backendDriver.getSequence({
|
|
105
|
+
assemblyName: sourceAssemblyId,
|
|
106
|
+
refName,
|
|
107
|
+
start: exon.min,
|
|
108
|
+
end: exon.max,
|
|
109
|
+
})
|
|
110
|
+
stitchedSequence += sourceFeature.strand === -1 ? revcom(seq) : seq
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const orf = findLongestOrf(stitchedSequence)
|
|
114
|
+
if (!orf) {
|
|
115
|
+
;(session as unknown as AbstractSessionModel).notify(
|
|
116
|
+
'No open reading frame found in this transcript',
|
|
117
|
+
'error',
|
|
118
|
+
)
|
|
119
|
+
handleClose()
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const strand = sourceFeature.strand ?? 1
|
|
124
|
+
if (strand === 1) {
|
|
125
|
+
cdsMin = stitchedToGenomicBound(orf[0], exonParts, 1)
|
|
126
|
+
cdsMax = stitchedToGenomicBound(orf[1] + 3, exonParts, 1)
|
|
127
|
+
} else {
|
|
128
|
+
cdsMax = stitchedToGenomicBound(orf[0], exonParts, -1)
|
|
129
|
+
cdsMin = stitchedToGenomicBound(orf[1] + 3, exonParts, -1)
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
cdsMin = Number(minInput) - 1
|
|
133
|
+
cdsMax = Number(maxInput)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const _id = new ObjectID().toHexString()
|
|
137
|
+
const addedFeature: AnnotationFeatureSnapshot = {
|
|
138
|
+
_id,
|
|
139
|
+
refSeq: sourceFeature.refSeq,
|
|
140
|
+
min: cdsMin,
|
|
141
|
+
max: cdsMax,
|
|
142
|
+
type: 'CDS',
|
|
143
|
+
}
|
|
144
|
+
if (sourceFeature.strand) {
|
|
145
|
+
addedFeature.strand = sourceFeature.strand
|
|
146
|
+
}
|
|
147
|
+
const change = new AddFeatureChange({
|
|
148
|
+
changedIds: [sourceFeature._id],
|
|
149
|
+
typeName: 'AddFeatureChange',
|
|
150
|
+
assembly: sourceAssemblyId,
|
|
151
|
+
addedFeature,
|
|
152
|
+
parentFeatureId: sourceFeature._id,
|
|
153
|
+
})
|
|
154
|
+
await changeManager.submit(change)
|
|
155
|
+
session.apolloSetSelectedFeature(_id)
|
|
156
|
+
handleClose()
|
|
157
|
+
} catch (error) {
|
|
158
|
+
setErrorMessage(
|
|
159
|
+
error instanceof Error ? error.message : 'An unknown error occurred',
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const manualError =
|
|
165
|
+
method === 'manual' &&
|
|
166
|
+
Boolean(minInput) &&
|
|
167
|
+
Boolean(maxInput) &&
|
|
168
|
+
Number(maxInput) <= Number(minInput)
|
|
169
|
+
|
|
170
|
+
const submitDisabled =
|
|
171
|
+
method === 'manual' ? !(minInput && maxInput) || manualError : false
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Dialog
|
|
175
|
+
open
|
|
176
|
+
title="Add coding sequence"
|
|
177
|
+
handleClose={handleClose}
|
|
178
|
+
maxWidth={false}
|
|
179
|
+
data-testid="add-coding-sequence"
|
|
180
|
+
>
|
|
181
|
+
<form
|
|
182
|
+
onSubmit={(event) => {
|
|
183
|
+
void onSubmit(event)
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
|
|
187
|
+
<FormControl>
|
|
188
|
+
<RadioGroup
|
|
189
|
+
value={method}
|
|
190
|
+
onChange={(e) => {
|
|
191
|
+
setMethod(e.target.value as 'longest-orf' | 'manual')
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
<FormControlLabel
|
|
195
|
+
value="longest-orf"
|
|
196
|
+
control={<Radio />}
|
|
197
|
+
label="Calculate longest open reading frame"
|
|
198
|
+
/>
|
|
199
|
+
<FormControlLabel
|
|
200
|
+
value="manual"
|
|
201
|
+
control={<Radio />}
|
|
202
|
+
label="Manual"
|
|
203
|
+
/>
|
|
204
|
+
</RadioGroup>
|
|
205
|
+
</FormControl>
|
|
206
|
+
{method === 'manual' ? (
|
|
207
|
+
<>
|
|
208
|
+
<TextField
|
|
209
|
+
margin="dense"
|
|
210
|
+
id="cds-min"
|
|
211
|
+
label="Min"
|
|
212
|
+
type="number"
|
|
213
|
+
fullWidth
|
|
214
|
+
variant="outlined"
|
|
215
|
+
value={minInput}
|
|
216
|
+
onChange={(e) => {
|
|
217
|
+
setMinInput(e.target.value)
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
<TextField
|
|
221
|
+
margin="dense"
|
|
222
|
+
id="cds-max"
|
|
223
|
+
label="Max"
|
|
224
|
+
type="number"
|
|
225
|
+
fullWidth
|
|
226
|
+
variant="outlined"
|
|
227
|
+
value={maxInput}
|
|
228
|
+
onChange={(e) => {
|
|
229
|
+
setMaxInput(e.target.value)
|
|
230
|
+
}}
|
|
231
|
+
error={manualError}
|
|
232
|
+
helperText={
|
|
233
|
+
manualError ? '"Max" must be greater than "Min"' : null
|
|
234
|
+
}
|
|
235
|
+
/>
|
|
236
|
+
</>
|
|
237
|
+
) : null}
|
|
238
|
+
</DialogContent>
|
|
239
|
+
<DialogActions>
|
|
240
|
+
<Button variant="contained" type="submit" disabled={submitDisabled}>
|
|
241
|
+
Submit
|
|
242
|
+
</Button>
|
|
243
|
+
<Button variant="outlined" type="button" onClick={handleClose}>
|
|
244
|
+
Cancel
|
|
245
|
+
</Button>
|
|
246
|
+
</DialogActions>
|
|
247
|
+
</form>
|
|
248
|
+
{errorMessage ? (
|
|
249
|
+
<DialogContent>
|
|
250
|
+
<DialogContentText color="error">{errorMessage}</DialogContentText>
|
|
251
|
+
</DialogContent>
|
|
252
|
+
) : null}
|
|
253
|
+
</Dialog>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
@@ -233,7 +233,7 @@ export function AddFeature({
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
let submitDisabled: boolean =
|
|
236
|
+
let submitDisabled: boolean = error || !(start && end && type)
|
|
237
237
|
if (
|
|
238
238
|
(type === NewFeature.CUSTOM && !customType) ||
|
|
239
239
|
(!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
|
|
@@ -68,7 +68,7 @@ export const AddRefSeqAliases = observer(function AddRefSeqAliases({
|
|
|
68
68
|
}[]
|
|
69
69
|
>([])
|
|
70
70
|
const [refNameAliasMap, setRefNameAliasMap] = useState<Map<string, string[]>>(
|
|
71
|
-
new Map(),
|
|
71
|
+
() => new Map(),
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
const { apolloDataStore } = session
|
|
@@ -83,6 +83,7 @@ export const AddRefSeqAliases = observer(function AddRefSeqAliases({
|
|
|
83
83
|
|
|
84
84
|
useEffect(() => {
|
|
85
85
|
if (assemblies.length > 0) {
|
|
86
|
+
// eslint-disable-next-line @eslint-react/set-state-in-effect
|
|
86
87
|
setSelectedAssembly(assemblies[0])
|
|
87
88
|
collaborationServerDriver
|
|
88
89
|
.getRefNameAliases(assemblies[0].name)
|
|
@@ -94,7 +95,7 @@ export const AddRefSeqAliases = observer(function AddRefSeqAliases({
|
|
|
94
95
|
setErrorMessage('Error fetching refName aliases for assembly')
|
|
95
96
|
})
|
|
96
97
|
}
|
|
97
|
-
// eslint-disable-next-line react
|
|
98
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
98
99
|
}, [])
|
|
99
100
|
|
|
100
101
|
const initializeRefNameAliasMap = (refNameAliasesList: RefNameAliases[]) => {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
AnnotationFeatureSnapshot,
|
|
9
9
|
} from '@apollo-annotation/mst'
|
|
10
10
|
import { AddFeatureChange } from '@apollo-annotation/shared'
|
|
11
|
+
import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
11
12
|
import { readConfObject } from '@jbrowse/core/configuration'
|
|
12
13
|
import type { AbstractSessionModel } from '@jbrowse/core/util'
|
|
13
14
|
import { getSnapshot } from '@jbrowse/mobx-state-tree'
|
|
@@ -105,7 +106,9 @@ export function CopyFeature({
|
|
|
105
106
|
setErrorMessage('No assemblies to copy to')
|
|
106
107
|
return
|
|
107
108
|
}
|
|
108
|
-
const assembly = await assemblyManager.waitForAssembly(
|
|
109
|
+
const assembly = (await assemblyManager.waitForAssembly(
|
|
110
|
+
selectedAssemblyId,
|
|
111
|
+
)) as Assembly | undefined
|
|
109
112
|
if (!assembly) {
|
|
110
113
|
return
|
|
111
114
|
}
|
|
@@ -136,7 +139,9 @@ export function CopyFeature({
|
|
|
136
139
|
event.preventDefault()
|
|
137
140
|
setErrorMessage('')
|
|
138
141
|
const featureLength = sourceFeature.length
|
|
139
|
-
const assembly = await assemblyManager.waitForAssembly(
|
|
142
|
+
const assembly = (await assemblyManager.waitForAssembly(
|
|
143
|
+
selectedAssemblyId,
|
|
144
|
+
)) as Assembly | undefined
|
|
140
145
|
if (!assembly) {
|
|
141
146
|
setErrorMessage(`Assembly not found: ${selectedAssemblyId}.`)
|
|
142
147
|
return
|
|
@@ -165,7 +170,7 @@ export function CopyFeature({
|
|
|
165
170
|
const featureIds: string[] = []
|
|
166
171
|
// Let's add featureId to each child recursively
|
|
167
172
|
const newFeatureLine = generateNewIds(
|
|
168
|
-
getSnapshot(sourceFeature)
|
|
173
|
+
getSnapshot(sourceFeature),
|
|
169
174
|
featureIds,
|
|
170
175
|
)
|
|
171
176
|
// Clear possible parentId -attribute.
|
|
@@ -193,10 +198,7 @@ export function CopyFeature({
|
|
|
193
198
|
min: newFeatureLine.min,
|
|
194
199
|
max: newFeatureLine.max,
|
|
195
200
|
type: newFeatureLine.type,
|
|
196
|
-
children: updatedChildren.children
|
|
197
|
-
string,
|
|
198
|
-
AnnotationFeatureSnapshot
|
|
199
|
-
>,
|
|
201
|
+
children: updatedChildren.children,
|
|
200
202
|
attributes: attributeMap,
|
|
201
203
|
strand: newFeatureLine.strand,
|
|
202
204
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable react
|
|
1
|
+
/* eslint-disable @eslint-react/set-state-in-effect */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
3
3
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
4
4
|
|
|
@@ -192,30 +192,35 @@ export function CreateApolloAnnotation({
|
|
|
192
192
|
const refSeq = apolloAssembly?.refSeqs.get(refSeqId)
|
|
193
193
|
const features = refSeq?.getFeatures(region.start, region.end)
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
const
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
const getDestinationFeatures = () => {
|
|
197
|
+
const filteredFeatures: AnnotationFeatureSnapshot[] = []
|
|
197
198
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
for (const f of features ?? []) {
|
|
200
|
+
if (f.min > region.end || f.max < region.start) {
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
// Destination feature should be of type gene
|
|
205
|
+
if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
|
|
206
|
+
const featureSnapshot = getSnapshot(f)
|
|
207
|
+
filteredFeatures.push(featureSnapshot)
|
|
208
|
+
}
|
|
207
209
|
}
|
|
208
|
-
}
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
useEffect(() => {
|
|
211
|
+
return filteredFeatures
|
|
212
|
+
}
|
|
214
213
|
setErrorMessage('')
|
|
215
|
-
const
|
|
216
|
-
setDestinationFeatures(
|
|
217
|
-
setSelectedDestinationFeature(
|
|
218
|
-
}, [
|
|
214
|
+
const destinationFeatures = getDestinationFeatures()
|
|
215
|
+
setDestinationFeatures(destinationFeatures)
|
|
216
|
+
setSelectedDestinationFeature(destinationFeatures[0])
|
|
217
|
+
}, [
|
|
218
|
+
checkedChildrens,
|
|
219
|
+
featureTypeOntology,
|
|
220
|
+
features,
|
|
221
|
+
parentFeatureChecked,
|
|
222
|
+
region,
|
|
223
|
+
])
|
|
219
224
|
|
|
220
225
|
const handleParentFeatureCheck = (
|
|
221
226
|
event: React.ChangeEvent<HTMLInputElement>,
|
|
@@ -349,7 +354,7 @@ export function CreateApolloAnnotation({
|
|
|
349
354
|
const copyGeneFeature = async () => {
|
|
350
355
|
const copiedAnnotationFeature = {
|
|
351
356
|
...annotationFeature,
|
|
352
|
-
}
|
|
357
|
+
}
|
|
353
358
|
removeSkippedAttributes(copiedAnnotationFeature, skippedAttributesOnCopy)
|
|
354
359
|
|
|
355
360
|
let change
|
|
@@ -394,7 +399,7 @@ export function CreateApolloAnnotation({
|
|
|
394
399
|
for (const transcriptId of Object.keys(transcripts)) {
|
|
395
400
|
const transcript = {
|
|
396
401
|
...transcripts[transcriptId],
|
|
397
|
-
}
|
|
402
|
+
}
|
|
398
403
|
removeSkippedAttributes(transcript, skippedAttributesOnCopy)
|
|
399
404
|
transcript.strand = selectedDestinationFeature.strand
|
|
400
405
|
|
|
@@ -422,7 +427,7 @@ export function CreateApolloAnnotation({
|
|
|
422
427
|
) => {
|
|
423
428
|
const copiedChildrens: Record<string, AnnotationFeatureSnapshot> = {}
|
|
424
429
|
for (const [childId, child] of Object.entries(childrens)) {
|
|
425
|
-
const copiedChild = { ...child }
|
|
430
|
+
const copiedChild = { ...child }
|
|
426
431
|
removeSkippedAttributes(copiedChild, skippedAttributesOnCopy)
|
|
427
432
|
copiedChildrens[childId] = copiedChild
|
|
428
433
|
}
|
|
@@ -40,7 +40,7 @@ export function DeleteAssembly({
|
|
|
40
40
|
}: DeleteAssemblyProps) {
|
|
41
41
|
const { internetAccounts } = getRoot<ApolloRootModel>(session)
|
|
42
42
|
const [errorMessage, setErrorMessage] = useState('')
|
|
43
|
-
const [confirmDelete,
|
|
43
|
+
const [confirmDelete, setConfirmDelete] = useState(false)
|
|
44
44
|
const [submitted, setSubmitted] = useState(false)
|
|
45
45
|
const apolloInternetAccounts = internetAccounts.filter(
|
|
46
46
|
(ia) => ia.type === 'ApolloInternetAccount',
|
|
@@ -151,7 +151,7 @@ export function DeleteAssembly({
|
|
|
151
151
|
<Checkbox
|
|
152
152
|
checked={confirmDelete}
|
|
153
153
|
onChange={() => {
|
|
154
|
-
|
|
154
|
+
setConfirmDelete(!confirmDelete)
|
|
155
155
|
}}
|
|
156
156
|
/>
|
|
157
157
|
}
|
|
@@ -330,10 +330,7 @@ export function DeleteFeature({
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
if (
|
|
334
|
-
featureTypeOntology &&
|
|
335
|
-
featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')
|
|
336
|
-
) {
|
|
333
|
+
if (featureTypeOntology?.isTypeOf(sourceFeature.type, 'exon')) {
|
|
337
334
|
const cdsChange = trimCDS(sourceFeature)
|
|
338
335
|
if (cdsChange) {
|
|
339
336
|
if (cdsChange.typeName === 'DeleteFeatureChange') {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
4
4
|
import { annotationFeatureToGFF3 } from '@apollo-annotation/shared'
|
|
5
5
|
import { GFFFormattingTransformer } from '@gmod/gff'
|
|
6
|
+
import type { Assembly } from '@jbrowse/core/assemblyManager/assembly'
|
|
6
7
|
import { getConf } from '@jbrowse/core/configuration'
|
|
7
8
|
import type { AbstractSessionModel } from '@jbrowse/core/util'
|
|
8
9
|
import {
|
|
@@ -49,7 +50,7 @@ export function DownloadGFF3({
|
|
|
49
50
|
session,
|
|
50
51
|
assembly: assemblyName,
|
|
51
52
|
}: DownloadGFF3Props) {
|
|
52
|
-
const [includeFASTA,
|
|
53
|
+
const [includeFASTA, setIncludeFASTA] = useState(false)
|
|
53
54
|
const [errorMessage, setErrorMessage] = useState('')
|
|
54
55
|
|
|
55
56
|
const { getInternetAccount } = session.apolloDataStore as {
|
|
@@ -61,7 +62,7 @@ export function DownloadGFF3({
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
const { assemblyManager } = session as unknown as AbstractSessionModel
|
|
64
|
-
const assembly = assemblyManager.get(assemblyName)
|
|
65
|
+
const assembly = assemblyManager.get(assemblyName) as Assembly | undefined
|
|
65
66
|
if (!assembly) {
|
|
66
67
|
setErrorMessage(`Assembly "${assemblyName}" not found`)
|
|
67
68
|
return
|
|
@@ -140,7 +141,7 @@ export function DownloadGFF3({
|
|
|
140
141
|
<Checkbox
|
|
141
142
|
checked={includeFASTA}
|
|
142
143
|
onChange={() => {
|
|
143
|
-
|
|
144
|
+
setIncludeFASTA(!includeFASTA)
|
|
144
145
|
}}
|
|
145
146
|
disabled={!internetAccountConfigId}
|
|
146
147
|
/>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
2
|
+
/* eslint-disable @typescript-eslint/unbound-method */
|
|
3
|
+
import type { AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
|
|
4
|
+
import {
|
|
5
|
+
AddFeatureChange,
|
|
6
|
+
DeleteFeatureChange,
|
|
7
|
+
} from '@apollo-annotation/shared'
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
DialogActions,
|
|
11
|
+
DialogContent,
|
|
12
|
+
DialogContentText,
|
|
13
|
+
FormControl,
|
|
14
|
+
FormControlLabel,
|
|
15
|
+
FormLabel,
|
|
16
|
+
Radio,
|
|
17
|
+
RadioGroup,
|
|
18
|
+
} from '@mui/material'
|
|
19
|
+
import ObjectID from 'bson-objectid'
|
|
20
|
+
import React, { useState } from 'react'
|
|
21
|
+
|
|
22
|
+
import type { ChangeManager } from '../ChangeManager'
|
|
23
|
+
|
|
24
|
+
import { Dialog } from './Dialog'
|
|
25
|
+
|
|
26
|
+
type Choice = 'keepExisting' | 'useNew' | 'keepBoth'
|
|
27
|
+
|
|
28
|
+
interface DuplicateFeatureDialogProps {
|
|
29
|
+
featureSnapshot: AnnotationFeatureSnapshot
|
|
30
|
+
existingFeature: AnnotationFeatureSnapshot
|
|
31
|
+
assemblyName: string
|
|
32
|
+
changeManager: ChangeManager
|
|
33
|
+
handleClose(): void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function reassignIds(
|
|
37
|
+
feature: AnnotationFeatureSnapshot,
|
|
38
|
+
): AnnotationFeatureSnapshot {
|
|
39
|
+
const newChildren: Record<string, AnnotationFeatureSnapshot> = {}
|
|
40
|
+
if (feature.children) {
|
|
41
|
+
for (const child of Object.values(feature.children)) {
|
|
42
|
+
const newChild = reassignIds(child)
|
|
43
|
+
newChildren[newChild._id] = newChild
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
...feature,
|
|
48
|
+
_id: new ObjectID().toHexString(),
|
|
49
|
+
children: feature.children ? newChildren : undefined,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function DuplicateFeatureDialog({
|
|
54
|
+
assemblyName,
|
|
55
|
+
changeManager,
|
|
56
|
+
existingFeature,
|
|
57
|
+
featureSnapshot,
|
|
58
|
+
handleClose,
|
|
59
|
+
}: DuplicateFeatureDialogProps) {
|
|
60
|
+
const [choice, setChoice] = useState<Choice>('keepExisting')
|
|
61
|
+
const [errorMessage, setErrorMessage] = useState('')
|
|
62
|
+
const [submitted, setSubmitted] = useState(false)
|
|
63
|
+
|
|
64
|
+
const handleSubmit = async () => {
|
|
65
|
+
setErrorMessage('')
|
|
66
|
+
setSubmitted(true)
|
|
67
|
+
try {
|
|
68
|
+
if (choice === 'useNew') {
|
|
69
|
+
const deleteChange = new DeleteFeatureChange({
|
|
70
|
+
typeName: 'DeleteFeatureChange',
|
|
71
|
+
assembly: assemblyName,
|
|
72
|
+
changedIds: [existingFeature._id],
|
|
73
|
+
deletedFeature: existingFeature,
|
|
74
|
+
})
|
|
75
|
+
await changeManager.submit(deleteChange)
|
|
76
|
+
const addChange = new AddFeatureChange({
|
|
77
|
+
typeName: 'AddFeatureChange',
|
|
78
|
+
assembly: assemblyName,
|
|
79
|
+
changedIds: [featureSnapshot._id],
|
|
80
|
+
addedFeature: featureSnapshot,
|
|
81
|
+
})
|
|
82
|
+
await changeManager.submit(addChange)
|
|
83
|
+
} else if (choice === 'keepBoth') {
|
|
84
|
+
const newSnapshot = reassignIds(featureSnapshot)
|
|
85
|
+
const addChange = new AddFeatureChange({
|
|
86
|
+
typeName: 'AddFeatureChange',
|
|
87
|
+
assembly: assemblyName,
|
|
88
|
+
changedIds: [newSnapshot._id],
|
|
89
|
+
addedFeature: newSnapshot,
|
|
90
|
+
})
|
|
91
|
+
await changeManager.submit(addChange)
|
|
92
|
+
}
|
|
93
|
+
handleClose()
|
|
94
|
+
} catch (error) {
|
|
95
|
+
setErrorMessage(String(error))
|
|
96
|
+
setSubmitted(false)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Dialog
|
|
102
|
+
open
|
|
103
|
+
title="Duplicate Feature Detected"
|
|
104
|
+
handleClose={handleClose}
|
|
105
|
+
maxWidth="sm"
|
|
106
|
+
fullWidth
|
|
107
|
+
>
|
|
108
|
+
<DialogContent>
|
|
109
|
+
<DialogContentText>
|
|
110
|
+
A feature with ID “{featureSnapshot._id}” already exists.
|
|
111
|
+
How would you like to resolve this conflict?
|
|
112
|
+
</DialogContentText>
|
|
113
|
+
<FormControl component="fieldset" sx={{ mt: 2 }}>
|
|
114
|
+
<FormLabel component="legend">Resolution</FormLabel>
|
|
115
|
+
<RadioGroup
|
|
116
|
+
value={choice}
|
|
117
|
+
onChange={(e) => {
|
|
118
|
+
setChoice(e.target.value as Choice)
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<FormControlLabel
|
|
122
|
+
value="keepExisting"
|
|
123
|
+
control={<Radio />}
|
|
124
|
+
label="Keep the existing feature"
|
|
125
|
+
/>
|
|
126
|
+
<FormControlLabel
|
|
127
|
+
value="useNew"
|
|
128
|
+
control={<Radio />}
|
|
129
|
+
label="Replace with the new feature"
|
|
130
|
+
/>
|
|
131
|
+
<FormControlLabel
|
|
132
|
+
value="keepBoth"
|
|
133
|
+
control={<Radio />}
|
|
134
|
+
label="Keep both (assign new IDs to the incoming feature)"
|
|
135
|
+
/>
|
|
136
|
+
</RadioGroup>
|
|
137
|
+
</FormControl>
|
|
138
|
+
{errorMessage ? (
|
|
139
|
+
<DialogContentText color="error" sx={{ mt: 1 }}>
|
|
140
|
+
{errorMessage}
|
|
141
|
+
</DialogContentText>
|
|
142
|
+
) : null}
|
|
143
|
+
</DialogContent>
|
|
144
|
+
<DialogActions>
|
|
145
|
+
<Button variant="contained" onClick={handleSubmit} disabled={submitted}>
|
|
146
|
+
Confirm
|
|
147
|
+
</Button>
|
|
148
|
+
<Button onClick={handleClose} disabled={submitted}>
|
|
149
|
+
Cancel
|
|
150
|
+
</Button>
|
|
151
|
+
</DialogActions>
|
|
152
|
+
</Dialog>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
@@ -63,8 +63,8 @@ export const FilterTranscripts = observer(function FilterTranscripts({
|
|
|
63
63
|
<Grid size={8}>
|
|
64
64
|
<FormGroup>
|
|
65
65
|
{allTranscripts.map((item) => (
|
|
66
|
-
// eslint-disable-next-line react/jsx-key
|
|
67
66
|
<FormControlLabel
|
|
67
|
+
key={item}
|
|
68
68
|
control={
|
|
69
69
|
<Checkbox
|
|
70
70
|
checked={!excludedTranscripts.includes(item)}
|