Skip to content

Commit 15e4c9d

Browse files
committed
Update
1 parent 606648f commit 15e4c9d

6 files changed

Lines changed: 165 additions & 51 deletions

File tree

src/LinearMafDisplay/components/GetSequenceDialog/index.tsx

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
1-
import React, { useEffect, useState } from 'react'
1+
import React, { useState } from 'react'
22

33
import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
44
import { getSession } from '@jbrowse/core/util'
5-
import { Button, DialogActions, DialogContent, TextField } from '@mui/material'
5+
import {
6+
Button,
7+
DialogActions,
8+
DialogContent,
9+
TextField,
10+
ToggleButton,
11+
ToggleButtonGroup,
12+
Typography,
13+
} from '@mui/material'
614
import { observer } from 'mobx-react'
715
import { makeStyles } from 'tss-react/mui'
816

9-
import { fetchSequences } from '../../../util/fetchSequences'
17+
import { useSequences } from '../../../util/useSequences'
18+
1019
import type { LinearMafDisplayModel } from '../../stateModel'
1120

1221
const useStyles = makeStyles()({
1322
dialogContent: {
1423
width: '80em',
1524
},
1625
textAreaInput: {
17-
fontFamily: 'Courier New',
26+
fontFamily: 'monospace',
1827
whiteSpace: 'pre',
1928
overflowX: 'auto',
2029
},
@@ -23,7 +32,7 @@ const useStyles = makeStyles()({
2332
},
2433
})
2534

26-
const SequenceDialog = observer(function ({
35+
const GetSequenceDialog = observer(function ({
2736
onClose,
2837
model,
2938
selectionCoords,
@@ -35,37 +44,40 @@ const SequenceDialog = observer(function ({
3544
dragEndX: number
3645
}
3746
}) {
38-
const [sequence, setSequence] = useState<string>('')
39-
const [loading, setLoading] = useState(true)
40-
const [error, setError] = useState<unknown>()
41-
const sequenceTooLarge = sequence ? sequence.length > 1_000_000 : false
47+
const [showAllLetters, setShowAllLetters] = useState(true)
4248
const { classes } = useStyles()
43-
44-
useEffect(() => {
45-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
46-
;(async () => {
47-
if (!selectionCoords) {
48-
setLoading(false)
49-
return
50-
}
51-
52-
try {
53-
setLoading(true)
54-
setError(undefined)
55-
56-
setSequence(await fetchSequences({ model, selectionCoords }))
57-
} catch (e) {
58-
console.error(e)
59-
setError(e)
60-
} finally {
61-
setLoading(false)
62-
}
63-
})()
64-
}, [model, selectionCoords])
49+
const { sequence, loading, error } = useSequences({
50+
model,
51+
selectionCoords,
52+
showAllLetters,
53+
})
54+
const sequenceTooLarge = sequence ? sequence.length > 1_000_000 : false
6555

6656
return (
6757
<Dialog open onClose={onClose} title="Subsequence Data" maxWidth="xl">
6858
<DialogContent>
59+
<div
60+
style={{
61+
display: 'flex',
62+
alignItems: 'center',
63+
marginBottom: '16px',
64+
}}
65+
>
66+
<ToggleButtonGroup
67+
value={showAllLetters}
68+
exclusive
69+
size="small"
70+
onChange={(_event, newDisplayMode) => {
71+
if (newDisplayMode !== null) {
72+
setShowAllLetters(newDisplayMode)
73+
}
74+
}}
75+
>
76+
<ToggleButton value={true}>Show All Letters</ToggleButton>
77+
<ToggleButton value={false}>Show Only Differences</ToggleButton>
78+
</ToggleButtonGroup>
79+
</div>
80+
6981
{error ? (
7082
<ErrorMessage error={error} />
7183
) : (
@@ -156,4 +168,4 @@ const SequenceDialog = observer(function ({
156168
)
157169
})
158170

159-
export default SequenceDialog
171+
export default GetSequenceDialog

src/MafGetSequences/MafGetSequences.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
2020
sessionId: string
2121
headers?: Record<string, string>
2222
regions: Region[]
23-
bpPerPx: number
23+
showAllLetters: boolean
2424
},
2525
rpcDriverClassName: string,
2626
) {
2727
const deserializedArgs = await this.deserializeArguments(
2828
args,
2929
rpcDriverClassName,
3030
)
31-
const { samples, regions, adapterConfig, sessionId } = deserializedArgs
31+
const { samples, regions, adapterConfig, sessionId, showAllLetters } =
32+
deserializedArgs
3233
const { dataAdapter } = await getAdapter(
3334
this.pluginManager,
3435
sessionId,
@@ -38,7 +39,8 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
3839
throw new Error('No regions selected')
3940
}
4041

41-
// Get features from the adapter
42+
console.log({ showAllLetters })
43+
4244
const featuresObservable = (
4345
dataAdapter as BaseFeatureDataAdapter
4446
).getFeatures(regions[0]!, deserializedArgs)
@@ -47,6 +49,7 @@ export default class MafGetSequences extends RpcMethodTypeWithFiltersAndRenameRe
4749
features: new Map(features.map(f => [f.id(), f])),
4850
samples,
4951
regions,
52+
showAllLetters,
5053
})
5154
}
5255
}

src/util/fastaUtils.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ const mockFeature = new SimpleFeature({
1616
assembly1: {
1717
chr: 'chr1',
1818
start: 100,
19-
data: 'ACGTACGTAC',
19+
seq: 'ACGTACGTAC',
2020
strand: 1,
2121
},
2222
assembly2: {
2323
chr: 'chr2',
2424
start: 200,
25-
data: 'AC-TTCGTAC',
25+
seq: 'AC-TTCGTAC',
2626
strand: 1,
2727
},
2828
},
@@ -71,13 +71,13 @@ test('gap in assembly1', () => {
7171
assembly1: {
7272
chr: 'chr1',
7373
start: 100,
74-
data: 'AC-TACGTAC',
74+
seq: 'AC-TACGTAC',
7575
strand: 1,
7676
},
7777
assembly2: {
7878
chr: 'chr2',
7979
start: 200,
80-
data: 'ACGTTCGTAC',
80+
seq: 'ACGTTCGTAC',
8181
strand: 1,
8282
},
8383
},

src/util/fastaUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ export function processFeaturesToFasta({
3232
const outputRows = samples.map(() => ' '.repeat(rlen))
3333
for (const feature of features.values()) {
3434
const leftCoord = feature.get('start')
35-
const vals = feature.get('alignments') as Record<string, { data: string }>
35+
const vals = feature.get('alignments') as Record<string, { seq: string }>
3636
const seq = feature.get('seq')
3737
console.log({ feature, seq })
3838
const r = Object.entries(vals)
3939
for (const [sample, val] of r) {
40-
const origAlignment = val.data
40+
const origAlignment = val.seq
4141
const alignment = origAlignment
4242

4343
const row = sampleToRowMap.get(sample)

src/util/fetchSequences.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
import { getContainingView, getSession } from '@jbrowse/core/util'
22
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
3-
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
3+
44
import type { LinearMafDisplayModel } from '../LinearMafDisplay/stateModel'
5+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
6+
7+
interface SelectionCoords {
8+
dragStartX: number
9+
dragEndX: number
10+
}
511

612
/**
713
* Fetch sequences for the given selection coordinates
814
* @param model - The LinearMafDisplayModel
915
* @param selectionCoords - The selection coordinates (dragStartX and dragEndX)
16+
* @param showAllLetters - Whether to show all letters or just the differences
1017
* @returns Promise that resolves to the FASTA sequence
1118
*/
1219
export async function fetchSequences({
1320
model,
1421
selectionCoords,
22+
showAllLetters,
1523
}: {
1624
model: LinearMafDisplayModel
17-
selectionCoords: {
18-
dragStartX: number
19-
dragEndX: number
20-
}
21-
}): Promise<string> {
25+
selectionCoords: SelectionCoords
26+
showAllLetters: boolean
27+
}) {
2228
const { samples, adapterConfig } = model
2329
const { rpcManager } = getSession(model)
2430
const sessionId = getRpcSessionId(model)
@@ -30,19 +36,22 @@ export async function fetchSequences({
3036
Math.max(dragStartX, dragEndX),
3137
]
3238

33-
const fastaSequence = await rpcManager.call(sessionId, 'MafGetSequences', {
39+
const fastaSequence = (await rpcManager.call(sessionId, 'MafGetSequences', {
3440
sessionId,
3541
adapterConfig,
3642
samples,
43+
showAllLetters,
3744
regions: [
3845
{
3946
refName,
40-
start: view.pxToBp(s).coord,
47+
start: view.pxToBp(s).coord - 1,
4148
end: view.pxToBp(e).coord,
4249
assemblyName,
4350
},
4451
],
45-
})
52+
})) as string[]
4653

47-
return fastaSequence as string
54+
return fastaSequence
55+
.map((r, idx) => `>${samples![idx]!.label}\n${r}`)
56+
.join('\n')
4857
}

src/util/useSequences.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useEffect, useState } from 'react'
2+
3+
import { getContainingView, getSession } from '@jbrowse/core/util'
4+
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
5+
6+
import type { LinearMafDisplayModel } from '../LinearMafDisplay/stateModel'
7+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
8+
9+
interface SelectionCoords {
10+
dragStartX: number
11+
dragEndX: number
12+
}
13+
14+
interface UseSequencesOptions {
15+
model: LinearMafDisplayModel
16+
selectionCoords?: SelectionCoords
17+
showAllLetters: boolean
18+
}
19+
20+
/**
21+
* React hook to fetch sequences for the given selection coordinates
22+
* @param options - The options for fetching sequences
23+
* @returns An object containing the sequence, loading state, and error
24+
*/
25+
export function useSequences({
26+
model,
27+
selectionCoords,
28+
showAllLetters,
29+
}: UseSequencesOptions) {
30+
const [sequence, setSequence] = useState<string>('')
31+
const [loading, setLoading] = useState(true)
32+
const [error, setError] = useState<unknown>()
33+
34+
useEffect(() => {
35+
// If no selection coordinates, no need to fetch
36+
if (!selectionCoords) {
37+
return
38+
}
39+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
40+
;(async () => {
41+
try {
42+
setLoading(true)
43+
setError(undefined)
44+
45+
const { samples, adapterConfig } = model
46+
const { rpcManager } = getSession(model)
47+
const sessionId = getRpcSessionId(model)
48+
const view = getContainingView(model) as LinearGenomeViewModel
49+
const { refName, assemblyName } = view.displayedRegions[0]!
50+
const { dragStartX, dragEndX } = selectionCoords
51+
const [s, e] = [
52+
Math.min(dragStartX, dragEndX),
53+
Math.max(dragStartX, dragEndX),
54+
]
55+
56+
const fastaSequence = (await rpcManager.call(
57+
sessionId,
58+
'MafGetSequences',
59+
{
60+
sessionId,
61+
adapterConfig,
62+
samples,
63+
showAllLetters,
64+
regions: [
65+
{
66+
refName,
67+
start: view.pxToBp(s).coord - 1,
68+
end: view.pxToBp(e).coord,
69+
assemblyName,
70+
},
71+
],
72+
},
73+
)) as string[]
74+
75+
const formattedSequence = fastaSequence
76+
.map((r, idx) => `>${samples![idx]!.label}\n${r}`)
77+
.join('\n')
78+
79+
setSequence(formattedSequence)
80+
} catch (e) {
81+
console.error(e)
82+
setError(e)
83+
} finally {
84+
setLoading(false)
85+
}
86+
})()
87+
}, [model, selectionCoords, showAllLetters])
88+
89+
return { sequence, loading, error }
90+
}

0 commit comments

Comments
 (0)