@jbrowse/plugin-jobs-management 2.6.1
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/LICENSE +201 -0
- package/dist/JobsListWidget/components/CurrentJobCard.d.ts +7 -0
- package/dist/JobsListWidget/components/CurrentJobCard.js +58 -0
- package/dist/JobsListWidget/components/CurrentJobCard.js.map +1 -0
- package/dist/JobsListWidget/components/JobCard.d.ts +7 -0
- package/dist/JobsListWidget/components/JobCard.js +17 -0
- package/dist/JobsListWidget/components/JobCard.js.map +1 -0
- package/dist/JobsListWidget/components/JobsListWidget.d.ts +7 -0
- package/dist/JobsListWidget/components/JobsListWidget.js +58 -0
- package/dist/JobsListWidget/components/JobsListWidget.js.map +1 -0
- package/dist/JobsListWidget/index.d.ts +3 -0
- package/dist/JobsListWidget/index.js +13 -0
- package/dist/JobsListWidget/index.js.map +1 -0
- package/dist/JobsListWidget/model.d.ts +163 -0
- package/dist/JobsListWidget/model.js +80 -0
- package/dist/JobsListWidget/model.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/esm/JobsListWidget/components/CurrentJobCard.d.ts +7 -0
- package/esm/JobsListWidget/components/CurrentJobCard.js +33 -0
- package/esm/JobsListWidget/components/CurrentJobCard.js.map +1 -0
- package/esm/JobsListWidget/components/JobCard.d.ts +7 -0
- package/esm/JobsListWidget/components/JobCard.js +12 -0
- package/esm/JobsListWidget/components/JobCard.js.map +1 -0
- package/esm/JobsListWidget/components/JobsListWidget.d.ts +7 -0
- package/esm/JobsListWidget/components/JobsListWidget.js +53 -0
- package/esm/JobsListWidget/components/JobsListWidget.js.map +1 -0
- package/esm/JobsListWidget/index.d.ts +3 -0
- package/esm/JobsListWidget/index.js +5 -0
- package/esm/JobsListWidget/index.js.map +1 -0
- package/esm/JobsListWidget/model.d.ts +163 -0
- package/esm/JobsListWidget/model.js +76 -0
- package/esm/JobsListWidget/model.js.map +1 -0
- package/esm/index.d.ts +7 -0
- package/esm/index.js +45 -0
- package/esm/index.js.map +1 -0
- package/package.json +59 -0
- package/src/JobsListWidget/components/CurrentJobCard.tsx +73 -0
- package/src/JobsListWidget/components/JobCard.tsx +19 -0
- package/src/JobsListWidget/components/JobsListWidget.tsx +103 -0
- package/src/JobsListWidget/index.ts +5 -0
- package/src/JobsListWidget/model.ts +85 -0
- package/src/index.test.ts +3 -0
- package/src/index.ts +51 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import {
|
|
4
|
+
Box,
|
|
5
|
+
Button,
|
|
6
|
+
Card,
|
|
7
|
+
CardActions,
|
|
8
|
+
CardContent,
|
|
9
|
+
LinearProgress,
|
|
10
|
+
Typography,
|
|
11
|
+
} from '@mui/material'
|
|
12
|
+
import { NewJob } from '../model'
|
|
13
|
+
|
|
14
|
+
function CurrentJobCard({ job }: { job: NewJob }) {
|
|
15
|
+
const [clicked, setClicked] = useState(false)
|
|
16
|
+
return (
|
|
17
|
+
<Card variant="outlined">
|
|
18
|
+
<CardContent>
|
|
19
|
+
<Typography variant="body1">
|
|
20
|
+
<strong>{'Name: '}</strong>
|
|
21
|
+
{job.name}
|
|
22
|
+
</Typography>
|
|
23
|
+
<Typography variant="body1">
|
|
24
|
+
<strong>{'Message: '}</strong>
|
|
25
|
+
{job.statusMessage || 'No message provided'}
|
|
26
|
+
</Typography>
|
|
27
|
+
<Box
|
|
28
|
+
sx={{
|
|
29
|
+
display: 'flex',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
marginTop: 10,
|
|
32
|
+
marginBottom: 10,
|
|
33
|
+
marginLeft: 10,
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
{job.progressPct === 0 || job.progressPct === 100 ? (
|
|
37
|
+
<Box sx={{ width: '100%' }}>
|
|
38
|
+
<LinearProgress variant="indeterminate" />
|
|
39
|
+
</Box>
|
|
40
|
+
) : (
|
|
41
|
+
<>
|
|
42
|
+
<Box sx={{ width: '100%' }}>
|
|
43
|
+
<LinearProgress variant="determinate" value={job.progressPct} />
|
|
44
|
+
</Box>
|
|
45
|
+
<Box sx={{ m: 1 }}>
|
|
46
|
+
<Typography>{`${Math.round(
|
|
47
|
+
job.progressPct || 0,
|
|
48
|
+
)}%`}</Typography>
|
|
49
|
+
</Box>
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
</Box>
|
|
53
|
+
</CardContent>
|
|
54
|
+
{job.cancelCallback ? (
|
|
55
|
+
<CardActions>
|
|
56
|
+
<Button
|
|
57
|
+
variant="contained"
|
|
58
|
+
color="inherit"
|
|
59
|
+
disabled={clicked || job.progressPct === 0}
|
|
60
|
+
onClick={() => {
|
|
61
|
+
job.cancelCallback && job.cancelCallback()
|
|
62
|
+
setClicked(true)
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
Cancel
|
|
66
|
+
</Button>
|
|
67
|
+
</CardActions>
|
|
68
|
+
) : null}
|
|
69
|
+
</Card>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default observer(CurrentJobCard)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { Card, CardContent, Typography } from '@mui/material'
|
|
4
|
+
import { NewJob } from '../model'
|
|
5
|
+
|
|
6
|
+
function JobCard({ job }: { job: NewJob }) {
|
|
7
|
+
return (
|
|
8
|
+
<Card variant="outlined">
|
|
9
|
+
<CardContent>
|
|
10
|
+
<Typography variant="body1">
|
|
11
|
+
<strong>{'Name: '}</strong>
|
|
12
|
+
{job.name}
|
|
13
|
+
</Typography>
|
|
14
|
+
</CardContent>
|
|
15
|
+
</Card>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default observer(JobCard)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Accordion,
|
|
4
|
+
AccordionSummary,
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
Typography,
|
|
8
|
+
} from '@mui/material'
|
|
9
|
+
import { makeStyles } from 'tss-react/mui'
|
|
10
|
+
import { observer } from 'mobx-react'
|
|
11
|
+
|
|
12
|
+
// icons
|
|
13
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
14
|
+
|
|
15
|
+
import JobCard from './JobCard'
|
|
16
|
+
import CurrentJobCard from './CurrentJobCard'
|
|
17
|
+
import { JobsListModel, NewJob } from '../model'
|
|
18
|
+
|
|
19
|
+
const useStyles = makeStyles()(theme => ({
|
|
20
|
+
root: {
|
|
21
|
+
margin: theme.spacing(1),
|
|
22
|
+
},
|
|
23
|
+
expandIcon: {
|
|
24
|
+
color: theme.palette.tertiary.contrastText,
|
|
25
|
+
},
|
|
26
|
+
button: {
|
|
27
|
+
marginTop: theme.spacing(1),
|
|
28
|
+
marginRight: theme.spacing(1),
|
|
29
|
+
},
|
|
30
|
+
adminBadge: {
|
|
31
|
+
margin: '0.5em',
|
|
32
|
+
borderRadius: 3,
|
|
33
|
+
backgroundColor: theme.palette.quaternary.main,
|
|
34
|
+
padding: '1em',
|
|
35
|
+
display: 'flex',
|
|
36
|
+
alignContent: 'center',
|
|
37
|
+
},
|
|
38
|
+
}))
|
|
39
|
+
|
|
40
|
+
function JobsListWidget({ model }: { model: JobsListModel }) {
|
|
41
|
+
const { classes } = useStyles()
|
|
42
|
+
const { jobs, finished, queued } = model
|
|
43
|
+
return (
|
|
44
|
+
<div className={classes.root}>
|
|
45
|
+
<Accordion defaultExpanded>
|
|
46
|
+
<AccordionSummary
|
|
47
|
+
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
48
|
+
>
|
|
49
|
+
<Typography variant="h5">Jobs</Typography>
|
|
50
|
+
</AccordionSummary>
|
|
51
|
+
{jobs.length ? (
|
|
52
|
+
jobs.map((job: NewJob, index: number) => (
|
|
53
|
+
<CurrentJobCard job={job} key={`${JSON.stringify(job)}-${index}`} />
|
|
54
|
+
))
|
|
55
|
+
) : (
|
|
56
|
+
<Card variant="outlined">
|
|
57
|
+
<CardContent>
|
|
58
|
+
<Typography variant="body1">No jobs</Typography>
|
|
59
|
+
</CardContent>
|
|
60
|
+
</Card>
|
|
61
|
+
)}
|
|
62
|
+
</Accordion>
|
|
63
|
+
<Accordion defaultExpanded>
|
|
64
|
+
<AccordionSummary
|
|
65
|
+
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
66
|
+
>
|
|
67
|
+
<Typography variant="h5">Queued jobs</Typography>
|
|
68
|
+
</AccordionSummary>
|
|
69
|
+
{queued.length ? (
|
|
70
|
+
queued.map((job: NewJob, index: number) => (
|
|
71
|
+
<JobCard job={job} key={`${JSON.stringify(job)}-${index}`} />
|
|
72
|
+
))
|
|
73
|
+
) : (
|
|
74
|
+
<Card variant="outlined">
|
|
75
|
+
<CardContent>
|
|
76
|
+
<Typography variant="body1">No queued jobs</Typography>
|
|
77
|
+
</CardContent>
|
|
78
|
+
</Card>
|
|
79
|
+
)}
|
|
80
|
+
</Accordion>
|
|
81
|
+
<Accordion defaultExpanded>
|
|
82
|
+
<AccordionSummary
|
|
83
|
+
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
84
|
+
>
|
|
85
|
+
<Typography variant="h5">Jobs completed</Typography>
|
|
86
|
+
</AccordionSummary>
|
|
87
|
+
{finished.length ? (
|
|
88
|
+
finished.map((job: NewJob, index: number) => (
|
|
89
|
+
<JobCard key={`${JSON.stringify(job)}-${index}`} job={job} />
|
|
90
|
+
))
|
|
91
|
+
) : (
|
|
92
|
+
<Card variant="outlined">
|
|
93
|
+
<CardContent>
|
|
94
|
+
<Typography variant="body1">No jobs completed</Typography>
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
)}
|
|
98
|
+
</Accordion>
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default observer(JobsListWidget)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { types, Instance, SnapshotIn } from 'mobx-state-tree'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
+
import { ElementId } from '@jbrowse/core/util/types/mst'
|
|
4
|
+
|
|
5
|
+
export const Job = types
|
|
6
|
+
.model('Job', {
|
|
7
|
+
name: types.string,
|
|
8
|
+
statusMessage: types.maybe(types.string),
|
|
9
|
+
progressPct: types.number,
|
|
10
|
+
})
|
|
11
|
+
.volatile(self => ({
|
|
12
|
+
cancelCallback() {},
|
|
13
|
+
}))
|
|
14
|
+
.actions(self => ({
|
|
15
|
+
setCancelCallback(cancelCallback: () => void) {
|
|
16
|
+
self.cancelCallback = cancelCallback
|
|
17
|
+
},
|
|
18
|
+
setStatusMessage(message?: string) {
|
|
19
|
+
self.statusMessage = message
|
|
20
|
+
},
|
|
21
|
+
setProgressPct(pct: number) {
|
|
22
|
+
self.progressPct = pct
|
|
23
|
+
},
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
export interface NewJob extends SnapshotIn<typeof Job> {
|
|
27
|
+
cancelCallback(): void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default function f(pluginManager: PluginManager) {
|
|
31
|
+
return types
|
|
32
|
+
.model('JobsListModel', {
|
|
33
|
+
id: ElementId,
|
|
34
|
+
type: types.literal('JobsListWidget'),
|
|
35
|
+
jobs: types.array(Job),
|
|
36
|
+
finished: types.array(Job),
|
|
37
|
+
queued: types.array(Job),
|
|
38
|
+
})
|
|
39
|
+
.actions(self => ({
|
|
40
|
+
addJob(job: NewJob) {
|
|
41
|
+
const { cancelCallback } = job
|
|
42
|
+
const length = self.jobs.push(job)
|
|
43
|
+
const addedJob = self.jobs[length - 1]
|
|
44
|
+
addedJob.setCancelCallback(cancelCallback)
|
|
45
|
+
return addedJob
|
|
46
|
+
},
|
|
47
|
+
removeJob(jobName: string) {
|
|
48
|
+
const indx = self.jobs.findIndex(job => job.name === jobName)
|
|
49
|
+
const removed = self.jobs[indx]
|
|
50
|
+
self.jobs.splice(indx, 1)
|
|
51
|
+
return removed
|
|
52
|
+
},
|
|
53
|
+
addFinishedJob(job: NewJob) {
|
|
54
|
+
self.finished.push(job)
|
|
55
|
+
return self.finished
|
|
56
|
+
},
|
|
57
|
+
addQueuedJob(job: NewJob) {
|
|
58
|
+
self.queued.push(job)
|
|
59
|
+
return self.finished
|
|
60
|
+
},
|
|
61
|
+
removeQueuedJob(jobName: string) {
|
|
62
|
+
const indx = self.queued.findIndex(job => job.name === jobName)
|
|
63
|
+
const removed = self.queued[indx]
|
|
64
|
+
self.queued.splice(indx, 1)
|
|
65
|
+
return removed
|
|
66
|
+
},
|
|
67
|
+
updateJobStatusMessage(jobName: string, message?: string) {
|
|
68
|
+
const job = self.jobs.find(job => job.name === jobName)
|
|
69
|
+
if (!job) {
|
|
70
|
+
throw new Error(`No job found with name ${jobName}`)
|
|
71
|
+
}
|
|
72
|
+
job.setStatusMessage(message)
|
|
73
|
+
},
|
|
74
|
+
updateJobProgressPct(jobName: string, pct: number) {
|
|
75
|
+
const job = self.jobs.find(job => job.name === jobName)
|
|
76
|
+
if (!job) {
|
|
77
|
+
throw new Error(`No job found with name ${jobName}`)
|
|
78
|
+
}
|
|
79
|
+
job.setProgressPct(pct)
|
|
80
|
+
},
|
|
81
|
+
}))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type JobsListStateModel = ReturnType<typeof f>
|
|
85
|
+
export type JobsListModel = Instance<JobsListStateModel>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { lazy } from 'react'
|
|
2
|
+
import Plugin from '@jbrowse/core/Plugin'
|
|
3
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
4
|
+
import { SessionWithWidgets, isAbstractMenuManager } from '@jbrowse/core/util'
|
|
5
|
+
import { Indexing } from '@jbrowse/core/ui/Icons'
|
|
6
|
+
import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType'
|
|
7
|
+
import {
|
|
8
|
+
stateModelFactory as JobsListStateModelFactory,
|
|
9
|
+
configSchema as JobsListConfigSchema,
|
|
10
|
+
} from '../../jobs-management/src/JobsListWidget'
|
|
11
|
+
import { isSessionModelWithWidgets } from '@jbrowse/core/util'
|
|
12
|
+
|
|
13
|
+
export default class extends Plugin {
|
|
14
|
+
name = 'JobsManagementPlugin'
|
|
15
|
+
|
|
16
|
+
install(pluginManager: PluginManager) {
|
|
17
|
+
pluginManager.addWidgetType(() => {
|
|
18
|
+
return new WidgetType({
|
|
19
|
+
name: 'JobsListWidget',
|
|
20
|
+
heading: 'Running jobs',
|
|
21
|
+
configSchema: JobsListConfigSchema,
|
|
22
|
+
stateModel: JobsListStateModelFactory(pluginManager),
|
|
23
|
+
ReactComponent: lazy(
|
|
24
|
+
() =>
|
|
25
|
+
import(
|
|
26
|
+
'../../jobs-management/src/JobsListWidget/components/JobsListWidget'
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
configure(pluginManager: PluginManager) {
|
|
33
|
+
if (isAbstractMenuManager(pluginManager.rootModel)) {
|
|
34
|
+
pluginManager.rootModel.appendToMenu('Tools', {
|
|
35
|
+
label: 'Jobs list',
|
|
36
|
+
icon: Indexing, // TODO: pick a better icon
|
|
37
|
+
onClick: (session: SessionWithWidgets) => {
|
|
38
|
+
if (isSessionModelWithWidgets(session)) {
|
|
39
|
+
const { widgets } = session
|
|
40
|
+
let jobStatusWidget = widgets.get('JobsList')
|
|
41
|
+
if (!jobStatusWidget) {
|
|
42
|
+
jobStatusWidget = session.addWidget('JobsListWidget', 'JobsList')
|
|
43
|
+
} else {
|
|
44
|
+
session.showWidget(jobStatusWidget)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|