jbrowse-plugin-protein3d 0.0.2 → 0.0.3
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/LaunchProteinView/components/AlphaFoldDBSearch.js +44 -27
- package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js.map +1 -1
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.d.ts +7 -4
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +30 -11
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js.map +1 -1
- package/dist/LaunchProteinView/components/HelpDialog.js +10 -3
- package/dist/LaunchProteinView/components/HelpDialog.js.map +1 -1
- package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js +3 -7
- package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js.map +1 -1
- package/dist/LaunchProteinView/components/MSATable.d.ts +10 -0
- package/dist/LaunchProteinView/components/MSATable.js +53 -0
- package/dist/LaunchProteinView/components/MSATable.js.map +1 -0
- package/dist/LaunchProteinView/components/TranscriptSelector.d.ts +7 -3
- package/dist/LaunchProteinView/components/TranscriptSelector.js +22 -7
- package/dist/LaunchProteinView/components/TranscriptSelector.js.map +1 -1
- package/dist/LaunchProteinView/components/UserProvidedStructure.js +56 -43
- package/dist/LaunchProteinView/components/UserProvidedStructure.js.map +1 -1
- package/dist/LaunchProteinView/components/calculateProteinSequence.js.map +1 -0
- package/dist/LaunchProteinView/components/useIsoformProteinSequences.d.ts +14 -0
- package/dist/LaunchProteinView/{useProteinSequences.js → components/useIsoformProteinSequences.js} +11 -6
- package/dist/LaunchProteinView/components/useIsoformProteinSequences.js.map +1 -0
- package/dist/LaunchProteinView/components/useLocalStructureFileSequence.d.ts +7 -0
- package/dist/LaunchProteinView/components/useLocalStructureFileSequence.js +44 -0
- package/dist/LaunchProteinView/components/useLocalStructureFileSequence.js.map +1 -0
- package/dist/LaunchProteinView/{useMyGeneInfo.d.ts → components/useMyGeneInfoUniprotIdLookup.d.ts} +2 -2
- package/dist/LaunchProteinView/{useMyGeneInfo.js → components/useMyGeneInfoUniprotIdLookup.js} +15 -10
- package/dist/LaunchProteinView/components/useMyGeneInfoUniprotIdLookup.js.map +1 -0
- package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.d.ts +7 -0
- package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.js +42 -0
- package/dist/LaunchProteinView/components/useRemoteStructureFileSequence.js.map +1 -0
- package/dist/LaunchProteinView/{util.d.ts → components/util.d.ts} +0 -10
- package/dist/LaunchProteinView/{util.js → components/util.js} +3 -24
- package/dist/LaunchProteinView/components/util.js.map +1 -0
- package/dist/LaunchProteinView/index.js +1 -1
- package/dist/LaunchProteinView/index.js.map +1 -1
- package/dist/ProteinView/components/Header.js +1 -1
- package/dist/ProteinView/components/Header.js.map +1 -1
- package/dist/ProteinView/components/SplitString.js +1 -1
- package/dist/ProteinView/components/SplitString.js.map +1 -1
- package/dist/ProteinView/model.d.ts +8 -4
- package/dist/ProteinView/model.js +39 -13
- package/dist/ProteinView/model.js.map +1 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +147 -148
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
- package/package.json +2 -2
- package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +98 -47
- package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +65 -25
- package/src/LaunchProteinView/components/HelpDialog.tsx +35 -7
- package/src/LaunchProteinView/components/LaunchProteinViewDialog.tsx +1 -10
- package/src/LaunchProteinView/components/MSATable.tsx +96 -0
- package/src/LaunchProteinView/components/TranscriptSelector.tsx +36 -10
- package/src/LaunchProteinView/components/UserProvidedStructure.tsx +110 -64
- package/src/LaunchProteinView/{useProteinSequences.ts → components/useIsoformProteinSequences.ts} +12 -7
- package/src/LaunchProteinView/components/useLocalStructureFileSequence.ts +53 -0
- package/src/LaunchProteinView/{useMyGeneInfo.ts → components/useMyGeneInfoUniprotIdLookup.ts} +16 -11
- package/src/LaunchProteinView/components/useRemoteStructureFileSequence.ts +44 -0
- package/src/LaunchProteinView/{util.ts → components/util.ts} +3 -35
- package/src/LaunchProteinView/index.ts +1 -1
- package/src/ProteinView/components/Header.tsx +7 -5
- package/src/ProteinView/components/SplitString.tsx +1 -1
- package/src/ProteinView/model.ts +40 -12
- package/src/index.ts +1 -12
- package/dist/LaunchProteinView/calculateProteinSequence.js.map +0 -1
- package/dist/LaunchProteinView/components/PreLoadedStructureMapping.d.ts +0 -8
- package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js +0 -72
- package/dist/LaunchProteinView/components/PreLoadedStructureMapping.js.map +0 -1
- package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.d.ts +0 -7
- package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js +0 -26
- package/dist/LaunchProteinView/components/useCheckAlphaFoldDBExistence.js.map +0 -1
- package/dist/LaunchProteinView/useMyGeneInfo.js.map +0 -1
- package/dist/LaunchProteinView/useProteinSequences.d.ts +0 -10
- package/dist/LaunchProteinView/useProteinSequences.js.map +0 -1
- package/dist/LaunchProteinView/util.js.map +0 -1
- package/dist/ProteinModelSessionExtension.d.ts +0 -11
- package/dist/ProteinModelSessionExtension.js +0 -53
- package/dist/ProteinModelSessionExtension.js.map +0 -1
- package/src/LaunchProteinView/components/PreLoadedStructureMapping.tsx +0 -153
- package/src/LaunchProteinView/components/useCheckAlphaFoldDBExistence.ts +0 -31
- package/src/ProteinModelSessionExtension.ts +0 -71
- /package/dist/LaunchProteinView/{calculateProteinSequence.d.ts → components/calculateProteinSequence.d.ts} +0 -0
- /package/dist/LaunchProteinView/{calculateProteinSequence.js → components/calculateProteinSequence.js} +0 -0
- /package/src/LaunchProteinView/{calculateProteinSequence.ts → components/calculateProteinSequence.ts} +0 -0
|
@@ -3,13 +3,13 @@ import { observer } from 'mobx-react'
|
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
5
|
DialogActions,
|
|
6
|
-
Radio,
|
|
7
|
-
RadioGroup,
|
|
8
6
|
DialogContent,
|
|
9
|
-
TextField,
|
|
10
7
|
FormControlLabel,
|
|
11
8
|
FormControl,
|
|
12
9
|
Link,
|
|
10
|
+
Radio,
|
|
11
|
+
RadioGroup,
|
|
12
|
+
TextField,
|
|
13
13
|
Typography,
|
|
14
14
|
} from '@mui/material'
|
|
15
15
|
import { makeStyles } from 'tss-react/mui'
|
|
@@ -28,11 +28,17 @@ import {
|
|
|
28
28
|
getId,
|
|
29
29
|
getTranscriptDisplayName,
|
|
30
30
|
getTranscriptFeatures,
|
|
31
|
-
} from '
|
|
31
|
+
} from './util'
|
|
32
|
+
|
|
33
|
+
// components
|
|
32
34
|
import TranscriptSelector from './TranscriptSelector'
|
|
35
|
+
import MSATable from './MSATable'
|
|
36
|
+
import HelpButton from './HelpButton'
|
|
33
37
|
|
|
34
38
|
// hooks
|
|
35
|
-
import
|
|
39
|
+
import useIsoformProteinSequences from './useIsoformProteinSequences'
|
|
40
|
+
import useLocalStructureFileSequence from './useLocalStructureFileSequence'
|
|
41
|
+
import useRemoteStructureFileSequence from './useRemoteStructureFileSequence'
|
|
36
42
|
|
|
37
43
|
const useStyles = makeStyles()(theme => ({
|
|
38
44
|
dialogContent: {
|
|
@@ -73,62 +79,58 @@ const UserProvidedStructure = observer(function ({
|
|
|
73
79
|
const { classes } = useStyles()
|
|
74
80
|
const session = getSession(model)
|
|
75
81
|
const [file, setFile] = useState<File>()
|
|
82
|
+
const [pdbId, setPdbId] = useState('')
|
|
76
83
|
const [choice, setChoice] = useState('file')
|
|
77
84
|
const [error2, setError] = useState<unknown>()
|
|
78
85
|
const [structureURL, setStructureURL] = useState('')
|
|
79
|
-
const [
|
|
86
|
+
const [userSelection, setUserSelection] = useState<string>()
|
|
87
|
+
const [showAllProteinSequences, setShowAllProteinSequences] = useState(false)
|
|
80
88
|
|
|
81
89
|
// check if we are looking at a 'two-level' or 'three-level' feature by
|
|
82
90
|
// finding exon/CDS subfeatures. we want to select from transcript names
|
|
83
91
|
const options = getTranscriptFeatures(feature)
|
|
84
92
|
const view = getContainingView(model) as LGV
|
|
85
|
-
const selectedTranscript = options.find(val => getId(val) ===
|
|
86
|
-
const {
|
|
87
|
-
|
|
93
|
+
const selectedTranscript = options.find(val => getId(val) === userSelection)
|
|
94
|
+
const { isoformSequences, error } = useIsoformProteinSequences({
|
|
95
|
+
feature,
|
|
96
|
+
view,
|
|
97
|
+
})
|
|
98
|
+
const protein = isoformSequences?.[userSelection ?? '']
|
|
99
|
+
const { seq: structureSequence1, error: error3 } =
|
|
100
|
+
useLocalStructureFileSequence({ file })
|
|
101
|
+
|
|
102
|
+
const { seq: structureSequence2, error: error4 } =
|
|
103
|
+
useRemoteStructureFileSequence({ url: structureURL })
|
|
104
|
+
const structureName =
|
|
105
|
+
file?.name ??
|
|
106
|
+
structureURL.slice(structureURL.lastIndexOf('/') + 1) ??
|
|
107
|
+
'structureSequence'
|
|
108
|
+
const structureSequence = structureSequence1 ?? structureSequence2
|
|
109
|
+
|
|
88
110
|
useEffect(() => {
|
|
89
|
-
if (
|
|
90
|
-
|
|
111
|
+
if (isoformSequences !== undefined) {
|
|
112
|
+
const ret =
|
|
113
|
+
options.find(
|
|
114
|
+
f =>
|
|
115
|
+
isoformSequences[f.id()]?.seq.replaceAll('*', '') ==
|
|
116
|
+
structureSequence,
|
|
117
|
+
) ?? options.find(f => !!isoformSequences[f.id()])
|
|
118
|
+
setUserSelection(ret?.id())
|
|
91
119
|
}
|
|
92
|
-
}, [options,
|
|
120
|
+
}, [options, structureSequence, isoformSequences])
|
|
93
121
|
|
|
94
|
-
const e = error || error2
|
|
122
|
+
const e = error || error2 || error3 || error4
|
|
95
123
|
return (
|
|
96
124
|
<>
|
|
97
125
|
<DialogContent className={classes.dialogContent}>
|
|
98
126
|
{e ? <ErrorMessage error={e} /> : null}
|
|
99
127
|
<HelpText />
|
|
100
|
-
|
|
101
|
-
<>
|
|
102
|
-
<TranscriptSelector
|
|
103
|
-
val={selection ?? ''}
|
|
104
|
-
setVal={setSelection}
|
|
105
|
-
options={options}
|
|
106
|
-
feature={feature}
|
|
107
|
-
seqs={seqs}
|
|
108
|
-
/>
|
|
109
|
-
{selectedTranscript ? (
|
|
110
|
-
<TextField
|
|
111
|
-
variant="outlined"
|
|
112
|
-
multiline
|
|
113
|
-
minRows={5}
|
|
114
|
-
maxRows={10}
|
|
115
|
-
fullWidth
|
|
116
|
-
value={`>${selectedTranscript.get('name') || selectedTranscript.get('id')}\n${protein}`}
|
|
117
|
-
InputProps={{
|
|
118
|
-
readOnly: true,
|
|
119
|
-
classes: {
|
|
120
|
-
input: classes.textAreaFont,
|
|
121
|
-
},
|
|
122
|
-
}}
|
|
123
|
-
/>
|
|
124
|
-
) : null}
|
|
125
|
-
</>
|
|
126
|
-
) : (
|
|
127
|
-
<div style={{ margin: 20 }}>
|
|
128
|
-
<LoadingEllipses title="Loading protein sequences" variant="h6" />
|
|
129
|
-
</div>
|
|
130
|
-
)}
|
|
128
|
+
|
|
131
129
|
<div style={{ display: 'flex', margin: 30 }}>
|
|
130
|
+
<Typography>
|
|
131
|
+
Open your structure file <HelpButton />
|
|
132
|
+
</Typography>
|
|
133
|
+
|
|
132
134
|
<FormControl component="fieldset">
|
|
133
135
|
<RadioGroup
|
|
134
136
|
value={choice}
|
|
@@ -136,6 +138,11 @@ const UserProvidedStructure = observer(function ({
|
|
|
136
138
|
>
|
|
137
139
|
<FormControlLabel value="url" control={<Radio />} label="URL" />
|
|
138
140
|
<FormControlLabel value="file" control={<Radio />} label="File" />
|
|
141
|
+
<FormControlLabel
|
|
142
|
+
value="pdb"
|
|
143
|
+
control={<Radio />}
|
|
144
|
+
label="PDB ID"
|
|
145
|
+
/>
|
|
139
146
|
</RadioGroup>
|
|
140
147
|
</FormControl>
|
|
141
148
|
{choice === 'url' ? (
|
|
@@ -170,6 +177,56 @@ const UserProvidedStructure = observer(function ({
|
|
|
170
177
|
</Button>
|
|
171
178
|
</div>
|
|
172
179
|
) : null}
|
|
180
|
+
{choice === 'pdb' ? (
|
|
181
|
+
<TextField
|
|
182
|
+
value={pdbId}
|
|
183
|
+
onChange={event => {
|
|
184
|
+
const s = event.target.value
|
|
185
|
+
setPdbId(s)
|
|
186
|
+
setStructureURL(`https://files.rcsb.org/download/${s}.cif`)
|
|
187
|
+
}}
|
|
188
|
+
label="PDB ID"
|
|
189
|
+
/>
|
|
190
|
+
) : null}
|
|
191
|
+
</div>
|
|
192
|
+
<div style={{ margin: 20 }}>
|
|
193
|
+
{isoformSequences ? (
|
|
194
|
+
structureSequence ? (
|
|
195
|
+
<>
|
|
196
|
+
<TranscriptSelector
|
|
197
|
+
val={userSelection ?? ''}
|
|
198
|
+
setVal={setUserSelection}
|
|
199
|
+
structureSequence={structureSequence}
|
|
200
|
+
isoforms={options}
|
|
201
|
+
feature={feature}
|
|
202
|
+
isoformSequences={isoformSequences}
|
|
203
|
+
/>
|
|
204
|
+
<div style={{ margin: 10 }}>
|
|
205
|
+
<Button
|
|
206
|
+
variant="contained"
|
|
207
|
+
color="primary"
|
|
208
|
+
onClick={() =>
|
|
209
|
+
setShowAllProteinSequences(!showAllProteinSequences)
|
|
210
|
+
}
|
|
211
|
+
>
|
|
212
|
+
{showAllProteinSequences
|
|
213
|
+
? 'Hide all isoform protein sequences'
|
|
214
|
+
: 'Show all isoform protein sequences'}
|
|
215
|
+
</Button>
|
|
216
|
+
|
|
217
|
+
{showAllProteinSequences ? (
|
|
218
|
+
<MSATable
|
|
219
|
+
structureSequence={structureSequence}
|
|
220
|
+
structureName={structureName}
|
|
221
|
+
isoformSequences={isoformSequences}
|
|
222
|
+
/>
|
|
223
|
+
) : null}
|
|
224
|
+
</div>
|
|
225
|
+
</>
|
|
226
|
+
) : null
|
|
227
|
+
) : (
|
|
228
|
+
<LoadingEllipses title="Loading protein sequences" variant="h6" />
|
|
229
|
+
)}
|
|
173
230
|
</div>
|
|
174
231
|
</DialogContent>
|
|
175
232
|
<DialogActions>
|
|
@@ -188,26 +245,15 @@ const UserProvidedStructure = observer(function ({
|
|
|
188
245
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
189
246
|
;(async () => {
|
|
190
247
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
})
|
|
201
|
-
} else if (structureURL) {
|
|
202
|
-
session.addView('ProteinView', {
|
|
203
|
-
type: 'ProteinView',
|
|
204
|
-
url: structureURL,
|
|
205
|
-
seq2: protein,
|
|
206
|
-
feature: selectedTranscript?.toJSON(),
|
|
207
|
-
connectedViewId: view.id,
|
|
208
|
-
displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
|
|
209
|
-
})
|
|
210
|
-
}
|
|
248
|
+
session.addView('ProteinView', {
|
|
249
|
+
type: 'ProteinView',
|
|
250
|
+
seq2: protein,
|
|
251
|
+
feature: selectedTranscript?.toJSON(),
|
|
252
|
+
connectedViewId: view.id,
|
|
253
|
+
displayName: `Protein view ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
|
|
254
|
+
...(file ? { data: await file.text() } : {}),
|
|
255
|
+
...(structureURL ? { url: structureURL } : {}),
|
|
256
|
+
})
|
|
211
257
|
handleClose()
|
|
212
258
|
} catch (e) {
|
|
213
259
|
console.error(e)
|
package/src/LaunchProteinView/{useProteinSequences.ts → components/useIsoformProteinSequences.ts}
RENAMED
|
@@ -5,32 +5,37 @@ import { Feature } from '@jbrowse/core/util'
|
|
|
5
5
|
import { getTranscriptFeatures } from './util'
|
|
6
6
|
import { fetchProteinSeq } from './calculateProteinSequence'
|
|
7
7
|
|
|
8
|
-
export default function
|
|
8
|
+
export default function useIsoformProteinSequences({
|
|
9
9
|
feature,
|
|
10
10
|
view,
|
|
11
11
|
}: {
|
|
12
12
|
feature: Feature
|
|
13
|
-
view
|
|
13
|
+
view?: { assemblyNames?: string[] }
|
|
14
14
|
}) {
|
|
15
15
|
const [error, setError] = useState<unknown>()
|
|
16
|
-
const [
|
|
16
|
+
const [isoformSequences, setIsoformSequences] =
|
|
17
|
+
useState<Record<string, { feature: Feature; seq: string }>>()
|
|
18
|
+
const [isLoading, setLoading] = useState(false)
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
19
21
|
;(async () => {
|
|
20
22
|
try {
|
|
21
|
-
|
|
23
|
+
setLoading(true)
|
|
24
|
+
const ret = [] as [string, { feature: Feature; seq: string }][]
|
|
22
25
|
for (const f of getTranscriptFeatures(feature)) {
|
|
23
26
|
const seq = await fetchProteinSeq({ view, feature: f })
|
|
24
27
|
if (seq) {
|
|
25
|
-
ret.push([f.id(), seq])
|
|
28
|
+
ret.push([f.id(), { feature: f, seq }])
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
|
-
|
|
31
|
+
setIsoformSequences(Object.fromEntries(ret))
|
|
29
32
|
} catch (e) {
|
|
30
33
|
console.error(e)
|
|
31
34
|
setError(e)
|
|
35
|
+
} finally {
|
|
36
|
+
setLoading(false)
|
|
32
37
|
}
|
|
33
38
|
})()
|
|
34
39
|
}, [feature, view])
|
|
35
|
-
return {
|
|
40
|
+
return { isLoading, isoformSequences, error }
|
|
36
41
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { createPluginUI } from 'molstar/lib/mol-plugin-ui'
|
|
3
|
+
import { renderReact18 } from 'molstar/lib/mol-plugin-ui/react18'
|
|
4
|
+
import { loadStructureFromData } from '../../ProteinView/loadStructureFromData'
|
|
5
|
+
|
|
6
|
+
async function structureFileSequenceFetcher(
|
|
7
|
+
file: File,
|
|
8
|
+
format: 'pdb' | 'mmcif',
|
|
9
|
+
) {
|
|
10
|
+
const ret = document.createElement('div')
|
|
11
|
+
const p = await createPluginUI({
|
|
12
|
+
target: ret,
|
|
13
|
+
render: renderReact18,
|
|
14
|
+
})
|
|
15
|
+
const data = await file.text()
|
|
16
|
+
const { seq } = await loadStructureFromData({ data, plugin: p, format })
|
|
17
|
+
p.unmount()
|
|
18
|
+
ret.remove()
|
|
19
|
+
return seq
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default function useLocalStructureFileSequence({
|
|
23
|
+
file,
|
|
24
|
+
}: {
|
|
25
|
+
file?: File
|
|
26
|
+
}) {
|
|
27
|
+
const [error, setError] = useState<unknown>()
|
|
28
|
+
const [isLoading, setLoading] = useState(false)
|
|
29
|
+
const [seq, setSeq] = useState<string>()
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
32
|
+
;(async () => {
|
|
33
|
+
try {
|
|
34
|
+
if (file) {
|
|
35
|
+
setLoading(true)
|
|
36
|
+
|
|
37
|
+
const ext = file.name.slice(file.name.lastIndexOf('.') + 1) || 'pdb'
|
|
38
|
+
const seq = await structureFileSequenceFetcher(
|
|
39
|
+
file,
|
|
40
|
+
(ext === 'cif' ? 'mmcif' : ext) as 'pdb' | 'mmcif',
|
|
41
|
+
)
|
|
42
|
+
setSeq(seq)
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error(e)
|
|
46
|
+
setError(e)
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false)
|
|
49
|
+
}
|
|
50
|
+
})()
|
|
51
|
+
}, [file])
|
|
52
|
+
return { error, isLoading, seq }
|
|
53
|
+
}
|
package/src/LaunchProteinView/{useMyGeneInfo.ts → components/useMyGeneInfoUniprotIdLookup.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
|
-
import { jsonfetch } from '
|
|
2
|
+
import { jsonfetch } from '../../fetchUtils'
|
|
3
3
|
import { stripTrailingVersion } from './util'
|
|
4
4
|
|
|
5
5
|
interface MyGeneInfoResults {
|
|
@@ -13,25 +13,30 @@ interface MyGeneInfoResults {
|
|
|
13
13
|
export default function useMyGeneInfo({ id }: { id: string }) {
|
|
14
14
|
const [result, setResult] = useState<MyGeneInfoResults>()
|
|
15
15
|
const [error, setError] = useState<unknown>()
|
|
16
|
-
const [
|
|
16
|
+
const [isLoading, setLoading] = useState(false)
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
19
19
|
;(async () => {
|
|
20
20
|
try {
|
|
21
|
-
if (
|
|
22
|
-
|
|
21
|
+
if (id) {
|
|
22
|
+
setLoading(true)
|
|
23
|
+
const res = await jsonfetch(
|
|
24
|
+
`https://mygene.info/v3/query?q=${stripTrailingVersion(id)}&fields=uniprot,symbol`,
|
|
25
|
+
)
|
|
26
|
+
setResult(res)
|
|
23
27
|
}
|
|
24
|
-
setLoading(true)
|
|
25
|
-
const res = await jsonfetch(
|
|
26
|
-
`https://mygene.info/v3/query?q=${stripTrailingVersion(id)}&fields=uniprot,symbol`,
|
|
27
|
-
)
|
|
28
|
-
setLoading(false)
|
|
29
|
-
setResult(res)
|
|
30
28
|
} catch (e) {
|
|
31
29
|
console.error(e)
|
|
32
30
|
setError(e)
|
|
31
|
+
} finally {
|
|
32
|
+
setLoading(false)
|
|
33
33
|
}
|
|
34
34
|
})()
|
|
35
35
|
}, [id])
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
isLoading,
|
|
39
|
+
uniprotId: result?.hits[0]?.uniprot?.['Swiss-Prot'],
|
|
40
|
+
error,
|
|
41
|
+
}
|
|
37
42
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { createPluginUI } from 'molstar/lib/mol-plugin-ui'
|
|
3
|
+
import { renderReact18 } from 'molstar/lib/mol-plugin-ui/react18'
|
|
4
|
+
import { loadStructureFromURL } from '../../ProteinView/loadStructureFromURL'
|
|
5
|
+
|
|
6
|
+
async function structureFileSequenceFetcher(url: string) {
|
|
7
|
+
const ret = document.createElement('div')
|
|
8
|
+
const p = await createPluginUI({
|
|
9
|
+
target: ret,
|
|
10
|
+
render: renderReact18,
|
|
11
|
+
})
|
|
12
|
+
const { seq } = await loadStructureFromURL({ url, plugin: p })
|
|
13
|
+
p.unmount()
|
|
14
|
+
ret.remove()
|
|
15
|
+
return seq
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function useRemoteStructureFileSequence({
|
|
19
|
+
url,
|
|
20
|
+
}: {
|
|
21
|
+
url?: string
|
|
22
|
+
}) {
|
|
23
|
+
const [error, setError] = useState<unknown>()
|
|
24
|
+
const [isLoading, setLoading] = useState(false)
|
|
25
|
+
const [seq, setSeq] = useState<string>()
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
28
|
+
;(async () => {
|
|
29
|
+
try {
|
|
30
|
+
if (url) {
|
|
31
|
+
setLoading(true)
|
|
32
|
+
const seq = await structureFileSequenceFetcher(url)
|
|
33
|
+
setSeq(seq)
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error(e)
|
|
37
|
+
setError(e)
|
|
38
|
+
} finally {
|
|
39
|
+
setLoading(false)
|
|
40
|
+
}
|
|
41
|
+
})()
|
|
42
|
+
}, [url])
|
|
43
|
+
return { error, isLoading, seq }
|
|
44
|
+
}
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import { Feature } from '@jbrowse/core/util'
|
|
2
2
|
|
|
3
|
-
export interface Row {
|
|
4
|
-
gene_id: string
|
|
5
|
-
gene_id_version: string
|
|
6
|
-
transcript_id_version: string
|
|
7
|
-
transcript_id: string
|
|
8
|
-
pdb_id: string
|
|
9
|
-
refseq_mrna_predicted_id: string
|
|
10
|
-
refseq_mrna_id: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
3
|
export function getTranscriptFeatures(feature: Feature) {
|
|
14
4
|
// check if we are looking at a 'two-level' or 'three-level' feature by
|
|
15
5
|
// finding exon/CDS subfeatures. we want to select from transcript names
|
|
@@ -29,30 +19,8 @@ export function z(n: number) {
|
|
|
29
19
|
return n.toLocaleString('en-US')
|
|
30
20
|
}
|
|
31
21
|
|
|
32
|
-
export function createMapFromData(data?: Row[]) {
|
|
33
|
-
const map = new Map<string, string>()
|
|
34
|
-
if (data) {
|
|
35
|
-
for (const d of data) {
|
|
36
|
-
const { pdb_id, transcript_id, refseq_mrna_id, transcript_id_version } = d
|
|
37
|
-
if (!pdb_id) {
|
|
38
|
-
continue
|
|
39
|
-
}
|
|
40
|
-
if (transcript_id) {
|
|
41
|
-
map.set(transcript_id, pdb_id)
|
|
42
|
-
}
|
|
43
|
-
if (refseq_mrna_id) {
|
|
44
|
-
map.set(refseq_mrna_id, pdb_id)
|
|
45
|
-
}
|
|
46
|
-
if (transcript_id_version) {
|
|
47
|
-
map.set(transcript_id_version, pdb_id)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return map
|
|
52
|
-
}
|
|
53
|
-
|
|
54
22
|
export function getDisplayName(f: Feature): string {
|
|
55
|
-
return f.get('id')
|
|
23
|
+
return f.get('name') || f.get('id')
|
|
56
24
|
}
|
|
57
25
|
|
|
58
26
|
export function getId(val?: Feature): string {
|
|
@@ -62,13 +30,13 @@ export function getId(val?: Feature): string {
|
|
|
62
30
|
export function getTranscriptDisplayName(val?: Feature): string {
|
|
63
31
|
return val === undefined
|
|
64
32
|
? ''
|
|
65
|
-
: [val.get('name')
|
|
33
|
+
: [val.get('name') || val.get('id')].filter(f => !!f).join(' ')
|
|
66
34
|
}
|
|
67
35
|
|
|
68
36
|
export function getGeneDisplayName(val?: Feature): string {
|
|
69
37
|
return val === undefined
|
|
70
38
|
? ''
|
|
71
|
-
: [val.get('gene_name') || val.get('name')
|
|
39
|
+
: [val.get('gene_name') || val.get('name') || val.get('id')]
|
|
72
40
|
.filter(f => !!f)
|
|
73
41
|
.join(' ')
|
|
74
42
|
}
|
|
@@ -20,11 +20,13 @@ const ProteinViewHeader = observer(function ({
|
|
|
20
20
|
return (
|
|
21
21
|
<div>
|
|
22
22
|
<InformativeHeaderArea model={model} />
|
|
23
|
-
{showAlignment
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
{showAlignment ? (
|
|
24
|
+
alignment ? (
|
|
25
|
+
<ProteinAlignment model={model} />
|
|
26
|
+
) : (
|
|
27
|
+
<LoadingEllipses message="Loading pairwise alignment" />
|
|
28
|
+
)
|
|
29
|
+
) : null}
|
|
28
30
|
</div>
|
|
29
31
|
)
|
|
30
32
|
})
|
package/src/ProteinView/model.ts
CHANGED
|
@@ -32,6 +32,7 @@ type MaybeLGV = LGV | undefined
|
|
|
32
32
|
function stateModelFactory() {
|
|
33
33
|
return types
|
|
34
34
|
.compose(
|
|
35
|
+
'ProteinView',
|
|
35
36
|
BaseViewModel,
|
|
36
37
|
types.model({
|
|
37
38
|
/**
|
|
@@ -92,15 +93,15 @@ function stateModelFactory() {
|
|
|
92
93
|
/**
|
|
93
94
|
* #property
|
|
94
95
|
*/
|
|
95
|
-
showHighlight:
|
|
96
|
+
showHighlight: false,
|
|
96
97
|
/**
|
|
97
98
|
* #property
|
|
98
99
|
*/
|
|
99
|
-
zoomToBaseLevel:
|
|
100
|
+
zoomToBaseLevel: true,
|
|
100
101
|
/**
|
|
101
102
|
* #property
|
|
102
103
|
*/
|
|
103
|
-
showAlignment:
|
|
104
|
+
showAlignment: false,
|
|
104
105
|
}),
|
|
105
106
|
)
|
|
106
107
|
.volatile(() => ({
|
|
@@ -316,25 +317,52 @@ function stateModelFactory() {
|
|
|
316
317
|
get structureSeqHoverPos(): number | undefined {
|
|
317
318
|
return self.hoverPosition?.structureSeqPos
|
|
318
319
|
},
|
|
320
|
+
|
|
321
|
+
get exactMatch() {
|
|
322
|
+
const r1 = self.seq1?.replaceAll('*', '')
|
|
323
|
+
const r2 = self.seq2?.replaceAll('*', '')
|
|
324
|
+
return r1 === r2
|
|
325
|
+
},
|
|
319
326
|
}))
|
|
320
327
|
.actions(self => ({
|
|
321
328
|
afterAttach() {
|
|
322
|
-
// pairwise align transcript sequence to structure
|
|
329
|
+
// pairwise align transcript sequence to structure residues
|
|
323
330
|
addDisposer(
|
|
324
331
|
self,
|
|
325
332
|
autorun(async () => {
|
|
326
333
|
try {
|
|
327
|
-
const { seq1, seq2 } = self
|
|
334
|
+
const { seq1, seq2, exactMatch } = self
|
|
328
335
|
if (!!self.alignment || !seq1 || !seq2) {
|
|
329
336
|
return
|
|
330
337
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
const r1 = seq1.replaceAll('*', '')
|
|
339
|
+
const r2 = seq2.replaceAll('*', '')
|
|
340
|
+
if (!exactMatch) {
|
|
341
|
+
const alignment = await launchPairwiseAlignment({
|
|
342
|
+
seq1: r1,
|
|
343
|
+
seq2: r2,
|
|
344
|
+
algorithm: 'emboss_needle',
|
|
345
|
+
onProgress: arg => self.setProgress(arg),
|
|
346
|
+
})
|
|
347
|
+
self.setAlignment(alignment.alignment)
|
|
348
|
+
|
|
349
|
+
// showHighlight when we are
|
|
350
|
+
self.setShowHighlight(true)
|
|
351
|
+
self.setShowAlignment(true)
|
|
352
|
+
} else {
|
|
353
|
+
let consensus = ''
|
|
354
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
355
|
+
for (let i = 0; i < r1.length; i++) {
|
|
356
|
+
consensus += '|'
|
|
357
|
+
}
|
|
358
|
+
self.setAlignment({
|
|
359
|
+
consensus,
|
|
360
|
+
alns: [
|
|
361
|
+
{ id: 'seq1', seq: r1 },
|
|
362
|
+
{ id: 'seq2', seq: r2 },
|
|
363
|
+
],
|
|
364
|
+
})
|
|
365
|
+
}
|
|
338
366
|
} catch (e) {
|
|
339
367
|
console.error(e)
|
|
340
368
|
self.setError(e)
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import Plugin from '@jbrowse/core/Plugin'
|
|
2
2
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
// locals
|
|
5
5
|
import { version } from '../package.json'
|
|
6
6
|
import ProteinViewF from './ProteinView'
|
|
7
7
|
import LaunchProteinViewF from './LaunchProteinView'
|
|
8
8
|
import AddHighlightModelF from './AddHighlightModel'
|
|
9
|
-
import ProteinModelSessionExtension from './ProteinModelSessionExtension'
|
|
10
9
|
|
|
11
10
|
export default class ProteinViewer extends Plugin {
|
|
12
11
|
name = 'ProteinViewer'
|
|
@@ -16,16 +15,6 @@ export default class ProteinViewer extends Plugin {
|
|
|
16
15
|
ProteinViewF(pluginManager)
|
|
17
16
|
LaunchProteinViewF(pluginManager)
|
|
18
17
|
AddHighlightModelF(pluginManager)
|
|
19
|
-
|
|
20
|
-
pluginManager.addToExtensionPoint('Core-extendSession', session => {
|
|
21
|
-
return types.compose(
|
|
22
|
-
types.model({
|
|
23
|
-
proteinModel: types.optional(ProteinModelSessionExtension, {}),
|
|
24
|
-
}),
|
|
25
|
-
// @ts-expect-error
|
|
26
|
-
session,
|
|
27
|
-
)
|
|
28
|
-
})
|
|
29
18
|
}
|
|
30
19
|
|
|
31
20
|
configure(_pluginManager: PluginManager) {}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"calculateProteinSequence.js","sourceRoot":"","sources":["../../src/LaunchProteinView/calculateProteinSequence.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
declare const AutoForm: ({ model, feature, handleClose, }: {
|
|
4
|
-
model: AbstractTrackModel;
|
|
5
|
-
feature: Feature;
|
|
6
|
-
handleClose: () => void;
|
|
7
|
-
}) => React.JSX.Element;
|
|
8
|
-
export default AutoForm;
|