Skip to content

Commit 4eb8f2f

Browse files
committed
Updates
1 parent 69d7f7e commit 4eb8f2f

File tree

4 files changed

+209
-152
lines changed

4 files changed

+209
-152
lines changed

plugins/dotplot-view/src/DiagonalizeDotplotRpc.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,15 @@ async function diagonalizeRegions(
145145
}
146146
}
147147

148+
// Preserve regions without alignments at the end
149+
const regionsWithoutAlignments = currentRegions.filter(
150+
r => !newQueryRegions.some(nr => nr.refName === r.refName),
151+
)
152+
148153
updateProgress(100, 'Diagonalization complete!')
149154

150155
return {
151-
newRegions: newQueryRegions,
156+
newRegions: [...newQueryRegions, ...regionsWithoutAlignments],
152157
stats: {
153158
totalAlignments: alignments.length,
154159
regionsProcessed: queryOrdering.length,

plugins/dotplot-view/src/DotplotView/components/DiagonalizationProgressDialog.tsx

Lines changed: 178 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useEffect, useRef, useState } from 'react'
1+
import { useState } from 'react'
22

3-
import { Dialog } from '@jbrowse/core/ui'
3+
import { Dialog, ErrorMessage } from '@jbrowse/core/ui'
44
import { getSession } from '@jbrowse/core/util'
55
import { createStopToken, stopStopToken } from '@jbrowse/core/util/stopToken'
66
import {
@@ -11,9 +11,9 @@ import {
1111
Typography,
1212
} from '@mui/material'
1313
import { transaction } from 'mobx'
14-
import { getSnapshot } from 'mobx-state-tree'
1514
import { observer } from 'mobx-react'
1615

16+
import type { AbstractSessionModel } from '@jbrowse/core/util'
1717
import type { DotplotViewModel } from '../model'
1818

1919
interface Region {
@@ -34,6 +34,96 @@ interface DiagonalizationResult {
3434
}
3535
}
3636

37+
interface RunDiagonalizationArgs {
38+
model: Pick<
39+
DotplotViewModel,
40+
'tracks' | 'hview' | 'vview' | 'id' | 'type' | 'displayName'
41+
>
42+
session: AbstractSessionModel
43+
stopToken: string
44+
setProgress: (progress: number) => void
45+
setMessage: (message: string) => void
46+
}
47+
48+
async function runDiagonalization({
49+
model,
50+
session,
51+
stopToken,
52+
setProgress,
53+
setMessage,
54+
}: RunDiagonalizationArgs) {
55+
setProgress(0)
56+
setMessage('Preparing diagonalization...')
57+
58+
// Get first track's adapter config
59+
const track = model.tracks[0]
60+
if (!track) {
61+
throw new Error('No tracks found')
62+
}
63+
64+
const display = track.displays[0]
65+
if (!display) {
66+
throw new Error('No display found')
67+
}
68+
69+
// Call RPC method to run diagonalization on worker
70+
const result = (await session.rpcManager.call(
71+
model.id,
72+
'DiagonalizeDotplot',
73+
{
74+
sessionId: `diagonalize-${Date.now()}`,
75+
view: {
76+
hview: model.hview,
77+
vview: model.vview,
78+
},
79+
adapterConfig: display.adapterConfig,
80+
stopToken,
81+
statusCallback: (msg: string) => {
82+
setMessage(msg)
83+
// Estimate progress based on message
84+
if (msg.includes('Initializing')) {
85+
setProgress(5)
86+
} else if (msg.includes('Getting renderer')) {
87+
setProgress(10)
88+
} else if (msg.includes('Fetching features')) {
89+
setProgress(20)
90+
} else if (msg.includes('Extracting')) {
91+
setProgress(30)
92+
} else if (msg.includes('Running diagonalization')) {
93+
setProgress(40)
94+
} else if (msg.includes('Grouping')) {
95+
setProgress(50)
96+
} else if (msg.includes('Determining')) {
97+
setProgress(65)
98+
} else if (msg.includes('Sorting')) {
99+
setProgress(80)
100+
} else if (msg.includes('Building')) {
101+
setProgress(90)
102+
} else if (msg.includes('complete')) {
103+
setProgress(100)
104+
}
105+
},
106+
},
107+
)) as DiagonalizationResult
108+
109+
setMessage('Applying new layout...')
110+
setProgress(95)
111+
112+
// Apply the new ordering
113+
if (result.newRegions.length > 0) {
114+
transaction(() => {
115+
model.vview.setDisplayedRegions(result.newRegions)
116+
})
117+
setProgress(100)
118+
setMessage(
119+
`Diagonalization complete! Reordered ${result.stats.regionsReordered} regions, reversed ${result.stats.regionsReversed}`,
120+
)
121+
return result
122+
} else {
123+
throw new Error('No regions to reorder')
124+
}
125+
}
126+
37127
const DiagonalizationProgressDialog = observer(function ({
38128
handleClose,
39129
model,
@@ -45,158 +135,104 @@ const DiagonalizationProgressDialog = observer(function ({
45135
>
46136
}) {
47137
const [progress, setProgress] = useState(0)
48-
const [message, setMessage] = useState('Initializing...')
49-
const [error, setError] = useState<string>()
50-
const [isComplete, setIsComplete] = useState(false)
51-
const stopTokenRef = useRef(createStopToken())
52-
53-
useEffect(() => {
54-
// Run diagonalization on mount
55-
const runDiagonalization = async () => {
56-
const session = getSession(model)
57-
const stopToken = stopTokenRef.current
58-
59-
try {
60-
setMessage('Preparing diagonalization...')
61-
62-
// Get first track's adapter config
63-
const track = model.tracks[0]
64-
if (!track) {
65-
setError('No tracks found')
66-
setIsComplete(true)
67-
session.notify('No tracks found to diagonalize', 'warning')
68-
return
69-
}
70-
71-
const display = track.displays[0]
72-
if (!display) {
73-
setError('No display found')
74-
setIsComplete(true)
75-
session.notify('No display found', 'warning')
76-
return
77-
}
78-
79-
// Call RPC method to run diagonalization on worker
80-
const result = (await session.rpcManager.call(
81-
model.id,
82-
'DiagonalizeDotplot',
83-
{
84-
sessionId: `diagonalize-${Date.now()}`,
85-
view: {
86-
hview: getSnapshot(model.hview),
87-
vview: getSnapshot(model.vview),
88-
},
89-
adapterConfig: getSnapshot(display.adapterConfig),
90-
stopToken,
91-
statusCallback: (msg: string) => {
92-
setMessage(msg)
93-
// Estimate progress based on message
94-
if (msg.includes('Initializing')) {
95-
setProgress(5)
96-
} else if (msg.includes('Getting renderer')) {
97-
setProgress(10)
98-
} else if (msg.includes('Fetching features')) {
99-
setProgress(20)
100-
} else if (msg.includes('Extracting')) {
101-
setProgress(30)
102-
} else if (msg.includes('Running diagonalization')) {
103-
setProgress(40)
104-
} else if (msg.includes('Grouping')) {
105-
setProgress(50)
106-
} else if (msg.includes('Determining')) {
107-
setProgress(65)
108-
} else if (msg.includes('Sorting')) {
109-
setProgress(80)
110-
} else if (msg.includes('Building')) {
111-
setProgress(90)
112-
} else if (msg.includes('complete')) {
113-
setProgress(100)
114-
}
115-
},
116-
},
117-
)) as DiagonalizationResult
118-
119-
setMessage('Applying new layout...')
120-
setProgress(95)
121-
122-
// Apply the new ordering
123-
if (result.newRegions.length > 0) {
124-
transaction(() => {
125-
model.vview.setDisplayedRegions(result.newRegions)
126-
})
127-
setProgress(100)
128-
setMessage(
129-
`Diagonalization complete! Reordered ${result.stats.regionsReordered} regions, reversed ${result.stats.regionsReversed}`,
130-
)
131-
setIsComplete(true)
132-
133-
// Auto-close after success
134-
setTimeout(() => {
135-
handleClose()
136-
}, 2000)
137-
} else {
138-
setError('No regions to reorder')
139-
setIsComplete(true)
140-
session.notify('No regions found to reorder', 'warning')
141-
}
142-
} catch (err) {
143-
console.error('Diagonalization error:', err)
144-
const errMsg = `${err}`
145-
setError(errMsg.includes('aborted') ? 'Cancelled by user' : `Error: ${err}`)
146-
setIsComplete(true)
147-
if (!errMsg.includes('aborted')) {
148-
session.notify(`Diagonalization failed: ${err}`, 'error')
149-
}
150-
}
151-
}
152-
153-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
154-
runDiagonalization()
155-
156-
// Cleanup stop token on unmount
157-
return () => {
158-
stopStopToken(stopTokenRef.current)
138+
const [message, setMessage] = useState('Ready to start diagonalization')
139+
const [error, setError] = useState<unknown>()
140+
const [isRunning, setIsRunning] = useState(false)
141+
const [stopToken, setStopToken] = useState<string>()
142+
143+
const handleStart = async () => {
144+
const session = getSession(model)
145+
const token = createStopToken()
146+
setStopToken(token)
147+
148+
try {
149+
setIsRunning(true)
150+
await runDiagonalization({
151+
model,
152+
session,
153+
stopToken: token,
154+
setProgress,
155+
setMessage,
156+
})
157+
158+
// Auto-close after success
159+
setTimeout(() => {
160+
handleClose()
161+
}, 2000)
162+
} catch (err) {
163+
console.error(err)
164+
setError(err)
165+
} finally {
166+
setIsRunning(false)
159167
}
160-
}, [model, handleClose])
168+
}
161169

162170
const handleCancel = () => {
163-
stopStopToken(stopTokenRef.current)
171+
if (stopToken) {
172+
stopStopToken(stopToken)
173+
setStopToken(undefined)
174+
}
164175
handleClose()
165176
}
166177

178+
const handleDialogClose = () => {
179+
// Only allow closing if not running
180+
if (!isRunning) {
181+
handleClose()
182+
}
183+
}
184+
167185
return (
168-
<Dialog open title="Diagonalizing" onClose={handleCancel}>
186+
<Dialog
187+
open
188+
title="Diagonalize Dotplot"
189+
onClose={handleDialogClose}
190+
maxWidth="lg"
191+
>
169192
<DialogContent style={{ minWidth: 400 }}>
170-
<Typography
171-
variant="body1"
172-
gutterBottom
173-
color={error ? 'error' : 'inherit'}
174-
>
175-
{error || message}
176-
</Typography>
177-
<LinearProgress
178-
variant="determinate"
179-
value={progress}
180-
style={{ marginTop: 16 }}
181-
color={error ? 'error' : 'primary'}
182-
/>
183-
<Typography
184-
variant="caption"
185-
color="textSecondary"
186-
style={{ marginTop: 8, display: 'block' }}
187-
>
188-
{Math.round(progress)}% complete
189-
</Typography>
193+
{message ? <Typography>{message}</Typography> : null}
194+
{error ? <ErrorMessage error={error} /> : null}
195+
{isRunning ? (
196+
<>
197+
<LinearProgress
198+
variant="determinate"
199+
value={progress}
200+
style={{ marginTop: 16 }}
201+
color={error ? 'error' : 'primary'}
202+
/>
203+
<Typography
204+
variant="caption"
205+
color="textSecondary"
206+
style={{ marginTop: 8, display: 'block' }}
207+
>
208+
{Math.round(progress)}% complete
209+
</Typography>
210+
</>
211+
) : null}
190212
</DialogContent>
191213
<DialogActions>
192-
{!isComplete ? (
193-
<Button onClick={handleCancel} color="secondary">
214+
{!isRunning ? (
215+
<>
216+
<Button onClick={handleClose} color="secondary" variant="contained">
217+
Cancel
218+
</Button>
219+
<Button
220+
onClick={() => {
221+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
222+
handleStart()
223+
}}
224+
color="primary"
225+
variant="contained"
226+
>
227+
Start
228+
</Button>
229+
</>
230+
) : null}
231+
{isRunning ? (
232+
<Button onClick={handleCancel} color="secondary" variant="contained">
194233
Cancel
195234
</Button>
196235
) : null}
197-
<Button onClick={handleClose} color="primary" disabled={!isComplete}>
198-
{isComplete ? 'Done' : 'Processing...'}
199-
</Button>
200236
</DialogActions>
201237
</Dialog>
202238
)

0 commit comments

Comments
 (0)