mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-10-03 12:41:13 +08:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
f762c49cbe
19 files changed
+1059
-70
No files matched your search
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/ban-ts-comment": "off"
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "Bug report"
|
||||
about: Create a bug report to improve optuna-dashboard
|
||||
title: "[Bug]"
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "Feature request"
|
||||
about: Suggest an idea for new features in optuna-dashboard.
|
||||
title: "[Feature]"
|
||||
title: ""
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/typescript-checks.yml'
|
||||
- '.eslintrc.js'
|
||||
- '**.ts'
|
||||
- '**.tsx'
|
||||
jobs:
|
||||
|
||||
@@ -68,9 +68,3 @@ You can walk-through trials by filtering and sorting.
|
||||
## Submitting patches
|
||||
|
||||
If you want to contribute, please check [Developers Guide](./DEVELOPMENT.md).
|
||||
|
||||
|
||||
## Alternatives
|
||||
|
||||
* ['optuna dashboard' subcommand](https://optuna.readthedocs.io/en/stable/reference/cli.html#dashboard): Official Optuna dashboard based on Bokeh CLI.
|
||||
* [ytsmiling/optdash](https://github.com/ytsmiling/optdash): a third-party dashboard for optuna.
|
||||
@@ -37,7 +37,7 @@ export const actionCreator = () => {
|
||||
const updateStudyDetail = (studyId: number) => {
|
||||
getStudyDetailAPI(studyId)
|
||||
.then((study) => {
|
||||
let newVal = Object.assign({}, studyDetails)
|
||||
const newVal = Object.assign({}, studyDetails)
|
||||
newVal[studyId] = study
|
||||
setStudyDetails(newVal)
|
||||
})
|
||||
|
||||
@@ -161,8 +161,8 @@ export const createNewStudyAPI = (
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteStudyAPI = (studyId: number): Promise<{}> => {
|
||||
return axiosInstance.delete<{}>(`/api/studies/${studyId}`).then((res) => {
|
||||
export const deleteStudyAPI = (studyId: number) => {
|
||||
return axiosInstance.delete(`/api/studies/${studyId}`).then((res) => {
|
||||
return {}
|
||||
})
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { SnackbarProvider } from "notistack"
|
||||
import { StudyDetail } from "./StudyDetail"
|
||||
import { StudyList } from "./StudyList"
|
||||
|
||||
export const App: FC<{}> = () => {
|
||||
export const App: FC = () => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider maxSnack={3}>
|
||||
|
||||
@@ -48,6 +48,7 @@ interface DataGridColumn<T> {
|
||||
field: keyof T
|
||||
label: string
|
||||
sortable?: boolean
|
||||
less?: (i: number, j: number) => number
|
||||
filterable?: boolean
|
||||
toCellValue?: (rowIndex: number) => string | React.ReactNode
|
||||
padding?: "default" | "checkbox" | "none"
|
||||
@@ -71,7 +72,7 @@ function DataGrid<T>(props: {
|
||||
const { columns, rows, keyField, dense, collapseBody } = props
|
||||
let { initialRowsPerPage, rowsPerPageOption } = props
|
||||
const [order, setOrder] = React.useState<Order>("asc")
|
||||
const [orderBy, setOrderBy] = React.useState<keyof T>(keyField)
|
||||
const [orderBy, setOrderBy] = React.useState<number>(0) // index of columns
|
||||
const [page, setPage] = React.useState(0)
|
||||
const [filters, setFilters] = React.useState<RowFilter<T>[]>([])
|
||||
|
||||
@@ -124,14 +125,14 @@ function DataGrid<T>(props: {
|
||||
)
|
||||
|
||||
// Sorting
|
||||
const createSortHandler = (property: keyof T) => (
|
||||
const createSortHandler = (columnId: number) => (
|
||||
event: React.MouseEvent<unknown>
|
||||
) => {
|
||||
const isAsc = orderBy === property && order === "asc"
|
||||
const isAsc = orderBy === columnId && order === "asc"
|
||||
setOrder(isAsc ? "desc" : "asc")
|
||||
setOrderBy(property)
|
||||
setOrderBy(columnId)
|
||||
}
|
||||
const sortedRows = stableSort<T>(filteredRows, getComparator(order, orderBy))
|
||||
const sortedRows = stableSort<T>(filteredRows, order, orderBy, columns)
|
||||
const currentPageRows =
|
||||
rowsPerPage > 0
|
||||
? sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
||||
@@ -158,9 +159,9 @@ function DataGrid<T>(props: {
|
||||
>
|
||||
{column.sortable ? (
|
||||
<TableSortLabel
|
||||
active={orderBy === column.field}
|
||||
direction={orderBy === column.field ? order : "asc"}
|
||||
onClick={createSortHandler(column.field)}
|
||||
active={orderBy === index}
|
||||
direction={orderBy === index ? order : "asc"}
|
||||
onClick={createSortHandler(index)}
|
||||
>
|
||||
{column.label}
|
||||
{orderBy === column.field ? (
|
||||
@@ -300,28 +301,48 @@ function DataGridRow<T>(props: {
|
||||
|
||||
function getComparator<T>(
|
||||
order: Order,
|
||||
orderBy: keyof T
|
||||
columns: DataGridColumn<T>[],
|
||||
orderBy: number
|
||||
): (a: T, b: T) => number {
|
||||
return order === "desc"
|
||||
? (a, b) => descendingComparator<T>(a, b, orderBy)
|
||||
: (a, b) => -descendingComparator<T>(a, b, orderBy)
|
||||
? (a, b) => descendingComparator<T>(a, b, columns, orderBy)
|
||||
: (a, b) => -descendingComparator<T>(a, b, columns, orderBy)
|
||||
}
|
||||
|
||||
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||
if (b[orderBy] < a[orderBy]) {
|
||||
function descendingComparator<T>(
|
||||
a: T,
|
||||
b: T,
|
||||
columns: DataGridColumn<T>[],
|
||||
orderBy: number
|
||||
): number {
|
||||
const field = columns[orderBy].field
|
||||
if (b[field] < a[field]) {
|
||||
return -1
|
||||
}
|
||||
if (b[orderBy] > a[orderBy]) {
|
||||
if (b[field] > a[field]) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function stableSort<T>(array: T[], comparator: (a: T, b: T) => number) {
|
||||
function stableSort<T>(
|
||||
array: T[],
|
||||
order: Order,
|
||||
orderBy: number,
|
||||
columns: DataGridColumn<T>[]
|
||||
) {
|
||||
// TODO(c-bata): Refactor here by implementing as the same comparator interface.
|
||||
const less = columns[orderBy].less
|
||||
const comparator = getComparator(order, columns, orderBy)
|
||||
const stabilizedThis = array.map((el, index) => [el, index] as [T, number])
|
||||
stabilizedThis.sort((a, b) => {
|
||||
const order = comparator(a[0], b[0])
|
||||
if (order !== 0) return order
|
||||
if (less) {
|
||||
const result = order == "asc" ? -less(a[1], b[1]) : less(a[1], b[1])
|
||||
if (result !== 0) return result
|
||||
} else {
|
||||
const result = comparator(a[0], b[0])
|
||||
if (result !== 0) return result
|
||||
}
|
||||
return a[1] - b[1]
|
||||
})
|
||||
return stabilizedThis.map((el) => el[0])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as plotly from "plotly.js-dist"
|
||||
import * as plotly from "plotly.js-basic-dist"
|
||||
import React, { ChangeEvent, FC, useEffect, useState } from "react"
|
||||
import {
|
||||
Grid,
|
||||
@@ -199,7 +199,7 @@ const plotHistory = (
|
||||
plotly.react(plotDomId, [])
|
||||
return
|
||||
}
|
||||
let trialsForLinePlot: Trial[] = []
|
||||
const trialsForLinePlot: Trial[] = []
|
||||
let currentBest: number | null = null
|
||||
filteredTrials.forEach((item) => {
|
||||
if (currentBest === null) {
|
||||
@@ -228,9 +228,9 @@ const plotHistory = (
|
||||
: trial.datetime_complete!
|
||||
}
|
||||
|
||||
let xForLinePlot = trialsForLinePlot.map(getAxisX)
|
||||
const xForLinePlot = trialsForLinePlot.map(getAxisX)
|
||||
xForLinePlot.push(getAxisX(filteredTrials[filteredTrials.length - 1]))
|
||||
let yForLinePlot = trialsForLinePlot.map(
|
||||
const yForLinePlot = trialsForLinePlot.map(
|
||||
(t: Trial): number => t.values![objectiveId]
|
||||
)
|
||||
yForLinePlot.push(yForLinePlot[yForLinePlot.length - 1])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as plotly from "plotly.js-dist"
|
||||
import * as plotly from "plotly.js-basic-dist"
|
||||
import React, { FC, useEffect } from "react"
|
||||
|
||||
const plotDomId = "graph-intermediate-values"
|
||||
@@ -30,7 +30,7 @@ const plotIntermediateValue = (trials: Trial[]) => {
|
||||
return
|
||||
}
|
||||
|
||||
let filteredTrials = trials.filter(
|
||||
const filteredTrials = trials.filter(
|
||||
(t) => t.state === "Complete" || t.state === "Pruned"
|
||||
)
|
||||
const plotData: Partial<plotly.PlotData>[] = filteredTrials.map((trial) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as plotly from "plotly.js-dist"
|
||||
import * as plotly from "plotly.js-basic-dist"
|
||||
import React, { FC, useEffect } from "react"
|
||||
|
||||
const plotDomId = "graph-parallel-coordinate"
|
||||
@@ -30,7 +30,7 @@ const plotCoordinate = (trials: Trial[], objectiveId: number) => {
|
||||
plotly.react(plotDomId, [])
|
||||
return
|
||||
}
|
||||
let filteredTrials = trials.filter(
|
||||
const filteredTrials = trials.filter(
|
||||
(t) => t.state === "Complete" || t.state === "Pruned"
|
||||
)
|
||||
|
||||
@@ -50,7 +50,7 @@ const plotCoordinate = (trials: Trial[], objectiveId: number) => {
|
||||
const objectiveValues: number[] = filteredTrials.map(
|
||||
(t) => t.values![objectiveId]
|
||||
)
|
||||
let dimensions = [
|
||||
const dimensions = [
|
||||
{
|
||||
label: "Objective value",
|
||||
values: objectiveValues,
|
||||
|
||||
@@ -92,7 +92,7 @@ export const useStudyDetailValue = (studyId: number): StudyDetail | null => {
|
||||
return studyDetails[studyId] || null
|
||||
}
|
||||
|
||||
export const StudyDetail: FC<{}> = () => {
|
||||
export const StudyDetail: FC = () => {
|
||||
const classes = useStyles()
|
||||
const action = actionCreator()
|
||||
const { studyId } = useParams<ParamTypes>()
|
||||
@@ -129,7 +129,7 @@ export const StudyDetail: FC<{}> = () => {
|
||||
<div className={classes.grow} />
|
||||
<div
|
||||
className={classes.reload}
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
setOpenReloadIntervalSelect(!openReloadIntervalSelect)
|
||||
}}
|
||||
>
|
||||
@@ -221,7 +221,7 @@ const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({
|
||||
}) => {
|
||||
const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
|
||||
|
||||
let columns: DataGridColumn<Trial>[] = [
|
||||
const columns: DataGridColumn<Trial>[] = [
|
||||
{ field: "number", label: "Number", sortable: true, padding: "none" },
|
||||
{
|
||||
field: "state",
|
||||
@@ -237,7 +237,26 @@ const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({
|
||||
field: "values",
|
||||
label: "Value",
|
||||
sortable: true,
|
||||
toCellValue: (i) => trials[i].values?.[0] || null,
|
||||
less: (i, j): number => {
|
||||
const firstVal = trials[i].values?.[0]
|
||||
const secondVal = trials[j].values?.[0]
|
||||
|
||||
if (firstVal === secondVal) {
|
||||
return 0
|
||||
} else if (firstVal && secondVal) {
|
||||
return firstVal < secondVal ? 1 : -1
|
||||
} else if (firstVal) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
toCellValue: (i) => {
|
||||
if (trials[i].values === undefined) {
|
||||
return null
|
||||
}
|
||||
return trials[i].values?.[0]
|
||||
},
|
||||
})
|
||||
} else {
|
||||
const objectiveColumns: DataGridColumn<
|
||||
@@ -246,7 +265,26 @@ const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({
|
||||
field: "values",
|
||||
label: `Objective ${objectiveId}`,
|
||||
sortable: true,
|
||||
toCellValue: (i) => trials[i].values?.[objectiveId] || null,
|
||||
less: (i, j): number => {
|
||||
const firstVal = trials[i].values?.[objectiveId]
|
||||
const secondVal = trials[j].values?.[objectiveId]
|
||||
|
||||
if (firstVal === secondVal) {
|
||||
return 0
|
||||
} else if (firstVal && secondVal) {
|
||||
return firstVal < secondVal ? 1 : -1
|
||||
} else if (firstVal) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
toCellValue: (i) => {
|
||||
if (trials[i].values === undefined) {
|
||||
return null
|
||||
}
|
||||
return trials[i].values?.[objectiveId]
|
||||
},
|
||||
}))
|
||||
columns.push(...objectiveColumns)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
})
|
||||
)
|
||||
|
||||
export const StudyList: FC<{}> = () => {
|
||||
export const StudyList: FC = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
const [
|
||||
@@ -127,7 +127,7 @@ export const StudyList: FC<{}> = () => {
|
||||
aria-label="delete study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
setDeleteStudyID(studies[i].study_id)
|
||||
setOpenDeleteStudyDialog(true)
|
||||
}}
|
||||
@@ -226,7 +226,7 @@ export const StudyList: FC<{}> = () => {
|
||||
<IconButton
|
||||
aria-controls="menu-appbar"
|
||||
aria-haspopup="true"
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
action.updateStudySummaries("Success to reload")
|
||||
}}
|
||||
color="inherit"
|
||||
@@ -256,12 +256,12 @@ export const StudyList: FC<{}> = () => {
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={openNewStudySelection}
|
||||
onClose={(e) => {
|
||||
onClose={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewSingleObjectiveStudyDialog(true)
|
||||
}}
|
||||
@@ -269,7 +269,7 @@ export const StudyList: FC<{}> = () => {
|
||||
Single-objective
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
setNewStudySelectionAnchorEl(null)
|
||||
setOpenNewMultiObjectiveStudyDialog(true)
|
||||
}}
|
||||
@@ -294,7 +294,7 @@ export const StudyList: FC<{}> = () => {
|
||||
</Container>
|
||||
<Dialog
|
||||
open={openNewSingleObjectiveStudyDialog}
|
||||
onClose={(e) => {
|
||||
onClose={() => {
|
||||
handleCloseNewSingleObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-single-objective-study-form-dialog-title"
|
||||
@@ -323,7 +323,7 @@ export const StudyList: FC<{}> = () => {
|
||||
control={
|
||||
<Checkbox
|
||||
checked={maximize}
|
||||
onChange={(e) => {
|
||||
onChange={() => {
|
||||
setMaximize(!maximize)
|
||||
}}
|
||||
color="primary"
|
||||
@@ -350,7 +350,7 @@ export const StudyList: FC<{}> = () => {
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openNewMultiObjectiveStudyDialog}
|
||||
onClose={(e) => {
|
||||
onClose={() => {
|
||||
handleCloseNewMultiObjectiveStudyDialog()
|
||||
}}
|
||||
aria-labelledby="create-multi-objective-study-form-dialog-title"
|
||||
@@ -384,7 +384,7 @@ export const StudyList: FC<{}> = () => {
|
||||
<Select
|
||||
value={directions[i]}
|
||||
onChange={(e) => {
|
||||
let newVal: StudyDirection[] = [...directions]
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal[i] = e.target.value as StudyDirection
|
||||
setDirections(newVal)
|
||||
}}
|
||||
@@ -400,7 +400,7 @@ export const StudyList: FC<{}> = () => {
|
||||
variant="outlined"
|
||||
startIcon={<Add />}
|
||||
className={classes.objectiveButton}
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions, "minimize"]
|
||||
setDirections(newVal)
|
||||
}}
|
||||
@@ -412,8 +412,8 @@ export const StudyList: FC<{}> = () => {
|
||||
startIcon={<Remove />}
|
||||
className={classes.objectiveButton}
|
||||
disabled={directions.length <= 1}
|
||||
onClick={(e) => {
|
||||
let newVal: StudyDirection[] = [...directions]
|
||||
onClick={() => {
|
||||
const newVal: StudyDirection[] = [...directions]
|
||||
newVal.pop()
|
||||
setDirections(newVal)
|
||||
}}
|
||||
@@ -443,7 +443,7 @@ export const StudyList: FC<{}> = () => {
|
||||
</Dialog>
|
||||
<Dialog
|
||||
open={openDeleteStudyDialog}
|
||||
onClose={(e) => {
|
||||
onClose={() => {
|
||||
handleCloseDeleteStudyDialog()
|
||||
}}
|
||||
aria-labelledby="delete-study-dialog-title"
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.2.2"
|
||||
__version__ = "0.2.3"
|
||||
Generated
+925
-7
File diff suppressed because it is too large.
Load diff
+6
-3
@@ -6,7 +6,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"fmt": "prettier --write \"optuna_dashboard/static/**/*.{ts,tsx}\"",
|
||||
"lint": "prettier --list-different \"optuna_dashboard/static/**/*.{ts,tsx}\"",
|
||||
"lint": "eslint . --ext .ts,.tsx && prettier --list-different \"optuna_dashboard/static/**/*.{ts,tsx}\"",
|
||||
"watch": "webpack --watch",
|
||||
"build": "webpack",
|
||||
"build:dev": "NODE_ENV=development webpack",
|
||||
@@ -19,7 +19,7 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"axios": "^0.21.1",
|
||||
"notistack": "^1.0.1",
|
||||
"plotly.js-dist": "^1.57.0",
|
||||
"plotly.js-basic-dist": "^1.58.4",
|
||||
"react": "^16.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
@@ -31,9 +31,12 @@
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-router-dom": "^5.1.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.0",
|
||||
"@typescript-eslint/parser": "^4.15.0",
|
||||
"eslint": "^7.19.0",
|
||||
"prettier": "^2.1.2",
|
||||
"ts-loader": "^8.0.6",
|
||||
"typescript": "^4.0.3",
|
||||
"typescript": "^4.1.4",
|
||||
"webpack": "^5.1.3",
|
||||
"webpack-cli": "^4.1.0"
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ setup(
|
||||
long_description_content_type="text/markdown",
|
||||
author="Masashi Shibata",
|
||||
author_email="m.shibata1020@gmail.com",
|
||||
url="https://github.com/c-bata/optuna-dashboard",
|
||||
url="https://github.com/optuna/optuna-dashboard",
|
||||
packages=find_packages(),
|
||||
install_requires=get_install_requires(),
|
||||
extras_require={
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"alwaysStrict": true,
|
||||
"outDir": "./optuna_dashboard/public/",
|
||||
"paths": {
|
||||
"plotly.js-dist": ["node_modules/@types/plotly.js"]
|
||||
"plotly.js-basic-dist": ["node_modules/@types/plotly.js"]
|
||||
},
|
||||
"noImplicitAny": true,
|
||||
"lib": ["dom", "esnext"],
|
||||
|
||||
Reference in new issue
Block a user