mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-09 11:28:14 +08:00
Rename components
This commit is contained in:
@@ -13,8 +13,8 @@ import {
|
||||
} from "@mui/material"
|
||||
|
||||
import { CompareStudies } from "./CompareStudies"
|
||||
import { StudyDetailBeta } from "./StudyDetailBeta"
|
||||
import { StudyListBeta } from "./StudyListBeta"
|
||||
import { StudyDetail } from "./StudyDetail"
|
||||
import { StudyList } from "./StudyList"
|
||||
|
||||
export const App: FC = () => {
|
||||
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)")
|
||||
@@ -54,7 +54,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/analytics"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"analytics"}
|
||||
/>
|
||||
@@ -63,7 +63,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trials"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialList"}
|
||||
/>
|
||||
@@ -72,7 +72,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trials"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialList"}
|
||||
/>
|
||||
@@ -81,7 +81,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/trialTable"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"trialTable"}
|
||||
/>
|
||||
@@ -90,7 +90,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/note"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"note"}
|
||||
/>
|
||||
@@ -99,7 +99,7 @@ export const App: FC = () => {
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
<StudyDetail
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"history"}
|
||||
/>
|
||||
@@ -113,7 +113,7 @@ export const App: FC = () => {
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/"}
|
||||
children={<StudyListBeta toggleColorMode={toggleColorMode} />}
|
||||
children={<StudyList toggleColorMode={toggleColorMode} />}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
|
||||
+2
-6
@@ -39,7 +39,7 @@ export const useURLVars = (): number => {
|
||||
return useMemo(() => parseInt(studyId, 10), [studyId])
|
||||
}
|
||||
|
||||
export const StudyDetailBeta: FC<{
|
||||
export const StudyDetail: FC<{
|
||||
toggleColorMode: () => void
|
||||
page: PageId
|
||||
}> = ({ toggleColorMode, page }) => {
|
||||
@@ -126,11 +126,7 @@ export const StudyDetailBeta: FC<{
|
||||
content = (
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<TrialTable
|
||||
studyDetail={studyDetail}
|
||||
isBeta={true}
|
||||
initialRowsPerPage={50}
|
||||
/>
|
||||
<TrialTable studyDetail={studyDetail} initialRowsPerPage={50} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
+1
-1
@@ -33,7 +33,7 @@ import { useCreateStudyDialog } from "./CreateStudyDialog"
|
||||
import { useDeleteStudyDialog } from "./DeleteStudyDialog"
|
||||
import { useRenameStudyDialog } from "./RenameStudyDialog"
|
||||
|
||||
export const StudyListBeta: FC<{
|
||||
export const StudyList: FC<{
|
||||
toggleColorMode: () => void
|
||||
}> = ({ toggleColorMode }) => {
|
||||
const theme = useTheme()
|
||||
@@ -1,28 +1,16 @@
|
||||
import React, { createRef, FC, FormEvent, MouseEvent } from "react"
|
||||
import {
|
||||
Typography,
|
||||
Grid,
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Stack,
|
||||
TextField,
|
||||
} from "@mui/material"
|
||||
import React, { FC } from "react"
|
||||
import { IconButton } from "@mui/material"
|
||||
import LinkIcon from "@mui/icons-material/Link"
|
||||
|
||||
import { DataGridColumn, DataGrid } from "./DataGrid"
|
||||
import { Link } from "react-router-dom"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
|
||||
export const TrialTable: FC<{
|
||||
studyDetail: StudyDetail | null
|
||||
isBeta: boolean
|
||||
initialRowsPerPage?: number
|
||||
}> = ({ studyDetail, isBeta, initialRowsPerPage }) => {
|
||||
}> = ({ studyDetail, initialRowsPerPage }) => {
|
||||
const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
|
||||
const objectiveNames: string[] = studyDetail?.objective_names || []
|
||||
const action = actionCreator()
|
||||
|
||||
const columns: DataGridColumn<Trial>[] = [
|
||||
{ field: "number", label: "Number", sortable: true, padding: "none" },
|
||||
@@ -103,48 +91,6 @@ export const TrialTable: FC<{
|
||||
}))
|
||||
columns.push(...objectiveColumns)
|
||||
}
|
||||
if (!isBeta) {
|
||||
columns.push({
|
||||
field: "datetime_start",
|
||||
label: "Duration(ms)",
|
||||
toCellValue: (i) => {
|
||||
const startMs = trials[i].datetime_start?.getTime()
|
||||
const completeMs = trials[i].datetime_complete?.getTime()
|
||||
if (startMs !== undefined && completeMs !== undefined) {
|
||||
return (completeMs - startMs).toString()
|
||||
}
|
||||
return null
|
||||
},
|
||||
sortable: true,
|
||||
less: (firstEl, secondEl): number => {
|
||||
const firstStartMs = firstEl.datetime_start?.getTime()
|
||||
const firstCompleteMs = firstEl.datetime_complete?.getTime()
|
||||
const firstDurationMs =
|
||||
firstStartMs !== undefined && firstCompleteMs !== undefined
|
||||
? firstCompleteMs - firstStartMs
|
||||
: undefined
|
||||
const secondStartMs = secondEl.datetime_start?.getTime()
|
||||
const secondCompleteMs = secondEl.datetime_complete?.getTime()
|
||||
const secondDurationMs =
|
||||
secondStartMs !== undefined && secondCompleteMs !== undefined
|
||||
? secondCompleteMs - secondStartMs
|
||||
: undefined
|
||||
|
||||
if (firstDurationMs === secondDurationMs) {
|
||||
return 0
|
||||
} else if (
|
||||
firstDurationMs !== undefined &&
|
||||
secondDurationMs !== undefined
|
||||
) {
|
||||
return firstDurationMs < secondDurationMs ? 1 : -1
|
||||
} else if (firstDurationMs !== undefined) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
if (
|
||||
studyDetail?.union_search_space.length ===
|
||||
studyDetail?.intersection_search_space.length
|
||||
@@ -220,175 +166,24 @@ export const TrialTable: FC<{
|
||||
},
|
||||
})
|
||||
})
|
||||
if (isBeta) {
|
||||
columns.push({
|
||||
field: "trial_id",
|
||||
label: "Detail",
|
||||
toCellValue: (i) => (
|
||||
<IconButton
|
||||
component={Link}
|
||||
to={
|
||||
URL_PREFIX +
|
||||
`/studies/${trials[i].study_id}/trials?numbers=${trials[i].number}`
|
||||
}
|
||||
color="inherit"
|
||||
title="Go to the trial's detail page"
|
||||
size="small"
|
||||
>
|
||||
<LinkIcon />
|
||||
</IconButton>
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
const collapseIntermediateValueColumns: DataGridColumn<TrialIntermediateValue>[] =
|
||||
[
|
||||
{ field: "step", label: "Step", sortable: true },
|
||||
{
|
||||
field: "value",
|
||||
label: "Value",
|
||||
sortable: true,
|
||||
less: (firstEl, secondEl): number => {
|
||||
const firstVal = firstEl.value
|
||||
const secondVal = secondEl.value
|
||||
if (firstVal === secondVal) {
|
||||
return 0
|
||||
}
|
||||
if (firstVal === "nan") {
|
||||
return -1
|
||||
} else if (secondVal === "nan") {
|
||||
return 1
|
||||
}
|
||||
if (firstVal === "-inf" || secondVal === "inf") {
|
||||
return 1
|
||||
} else if (secondVal === "-inf" || firstVal === "inf") {
|
||||
return -1
|
||||
}
|
||||
return firstVal < secondVal ? 1 : -1
|
||||
},
|
||||
},
|
||||
]
|
||||
const collapseAttrColumns: DataGridColumn<Attribute>[] = [
|
||||
{ field: "key", label: "Key", sortable: true },
|
||||
{ field: "value", label: "Value", sortable: true },
|
||||
]
|
||||
|
||||
const collapseBody = (index: number) => {
|
||||
const objectiveFormRefs = studyDetail?.directions.map((d) =>
|
||||
createRef<HTMLInputElement>()
|
||||
)
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {
|
||||
if (objectiveFormRefs === undefined) {
|
||||
return
|
||||
}
|
||||
if (studyDetail === null) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
const studyId = studyDetail.id
|
||||
const trialId = trials[index].trial_id
|
||||
const objectiveValues = objectiveFormRefs.map((ref) =>
|
||||
ref.current ? Number(ref.current.value) : NaN
|
||||
)
|
||||
if (objectiveValues.includes(NaN)) {
|
||||
return
|
||||
}
|
||||
|
||||
action.makeTrialComplete(studyId, trialId, objectiveValues)
|
||||
}
|
||||
|
||||
const handleFailTrial = (e: MouseEvent<HTMLButtonElement>): void => {
|
||||
if (studyDetail === null) {
|
||||
return
|
||||
}
|
||||
const studyId = studyDetail.id
|
||||
const trialId = trials[index].trial_id
|
||||
action.makeTrialFail(studyId, trialId)
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={6}>
|
||||
<Box margin={1}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Intermediate values
|
||||
</Typography>
|
||||
<DataGrid<TrialIntermediateValue>
|
||||
columns={collapseIntermediateValueColumns}
|
||||
rows={trials[index].intermediate_values}
|
||||
keyField={"step"}
|
||||
dense={true}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Box margin={1}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Trial system attributes
|
||||
</Typography>
|
||||
<DataGrid<Attribute>
|
||||
columns={collapseAttrColumns}
|
||||
rows={trials[index].system_attrs}
|
||||
keyField={"key"}
|
||||
dense={true}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
{trials[index].state === "Running" ? (
|
||||
<Grid item xs={12}>
|
||||
<Box margin={1}>
|
||||
<Typography variant="h6" gutterBottom component="div">
|
||||
Trial tell
|
||||
</Typography>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Box margin={1}>
|
||||
<Stack direction="row" spacing={1}>
|
||||
{objectiveFormRefs !== undefined &&
|
||||
objectiveFormRefs.map((ref, i) => (
|
||||
<TextField
|
||||
required
|
||||
id={`objective-${i}`}
|
||||
key={`objective-${i}`}
|
||||
label={
|
||||
objectiveNames.length ===
|
||||
studyDetail?.directions.length
|
||||
? objectiveNames[i]
|
||||
: `Objective ${i}`
|
||||
}
|
||||
inputProps={{
|
||||
inputMode: "numeric",
|
||||
pattern: "[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?",
|
||||
title: "Please input a float number",
|
||||
}}
|
||||
inputRef={ref}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box margin={1}>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Button variant="contained" type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="error"
|
||||
onClick={handleFailTrial}
|
||||
>
|
||||
Fail Trial
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
</form>
|
||||
</Box>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
columns.push({
|
||||
field: "trial_id",
|
||||
label: "Detail",
|
||||
toCellValue: (i) => (
|
||||
<IconButton
|
||||
component={Link}
|
||||
to={
|
||||
URL_PREFIX +
|
||||
`/studies/${trials[i].study_id}/trials?numbers=${trials[i].number}`
|
||||
}
|
||||
color="inherit"
|
||||
title="Go to the trial's detail page"
|
||||
size="small"
|
||||
>
|
||||
<LinkIcon />
|
||||
</IconButton>
|
||||
),
|
||||
})
|
||||
|
||||
return (
|
||||
<DataGrid<Trial>
|
||||
@@ -396,7 +191,6 @@ export const TrialTable: FC<{
|
||||
rows={trials}
|
||||
keyField={"trial_id"}
|
||||
dense={true}
|
||||
collapseBody={isBeta ? undefined : collapseBody}
|
||||
initialRowsPerPage={initialRowsPerPage}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
import React from "react"
|
||||
global.URL.createObjectURL = jest.fn()
|
||||
|
||||
import { SnackbarProvider } from "notistack"
|
||||
import { RecoilRoot } from "recoil"
|
||||
import { cleanup, render, within, fireEvent } from "@testing-library/react"
|
||||
import { TrialTable } from "../optuna_dashboard/ts/components/TrialTable"
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
const dummyDistribution: FloatDistribution = {
|
||||
type: "FloatDistribution",
|
||||
low: 0,
|
||||
high: 10,
|
||||
step: 1,
|
||||
log: false,
|
||||
}
|
||||
const trials: Trial[] = [
|
||||
{
|
||||
trial_id: 1,
|
||||
study_id: 0,
|
||||
number: 0,
|
||||
state: "Complete" as TrialState,
|
||||
values: [-1],
|
||||
intermediate_values: [],
|
||||
datetime_start: new Date("2021-06-15T00:00:00"),
|
||||
datetime_complete: new Date("2021-06-15T00:00:01"),
|
||||
params: [
|
||||
{
|
||||
name: "x",
|
||||
param_internal_value: 1,
|
||||
param_external_value: "1",
|
||||
param_external_type: "float",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
param_internal_value: 2,
|
||||
param_external_value: "2",
|
||||
param_external_type: "float",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
],
|
||||
fixed_params: [],
|
||||
user_attrs: [],
|
||||
system_attrs: [],
|
||||
note: {
|
||||
body: "",
|
||||
version: 0,
|
||||
},
|
||||
artifacts: [],
|
||||
},
|
||||
{
|
||||
trial_id: 2,
|
||||
study_id: 0,
|
||||
number: 1,
|
||||
state: "Fail" as TrialState,
|
||||
values: [-2],
|
||||
intermediate_values: [],
|
||||
datetime_start: new Date("2021-06-15T00:00:01"),
|
||||
datetime_complete: new Date("2021-06-15T00:00:03"),
|
||||
params: [
|
||||
{
|
||||
name: "x",
|
||||
param_internal_value: 1,
|
||||
param_external_value: "1",
|
||||
param_external_type: "float",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
param_internal_value: 2,
|
||||
param_external_value: "2",
|
||||
param_external_type: "float",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
],
|
||||
fixed_params: [],
|
||||
user_attrs: [],
|
||||
system_attrs: [],
|
||||
note: {
|
||||
body: "",
|
||||
version: 0,
|
||||
},
|
||||
artifacts: [],
|
||||
},
|
||||
]
|
||||
|
||||
const study_direction: StudyDirection = "minimize" as StudyDirection
|
||||
|
||||
const studyDetail: StudyDetail = {
|
||||
id: 1,
|
||||
name: "study_0",
|
||||
directions: [study_direction],
|
||||
datetime_start: new Date("2021-06-15T00:00:00"),
|
||||
best_trials: [trials[1]],
|
||||
trials: trials,
|
||||
intersection_search_space: [
|
||||
{
|
||||
name: "x",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
],
|
||||
union_search_space: [
|
||||
{
|
||||
name: "x",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
distribution: dummyDistribution,
|
||||
},
|
||||
],
|
||||
union_user_attrs: [
|
||||
{ key: "foo", sortable: false },
|
||||
{ key: "bar", sortable: false },
|
||||
],
|
||||
has_intermediate_values: false,
|
||||
note: {
|
||||
version: 0,
|
||||
body: "",
|
||||
},
|
||||
}
|
||||
|
||||
it("Sort TrialTable by trial number", () => {
|
||||
const { getAllByRole, getByText } = render(
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider>
|
||||
<TrialTable studyDetail={studyDetail} isBeta={false} />
|
||||
</SnackbarProvider>
|
||||
</RecoilRoot>
|
||||
)
|
||||
const rows = getAllByRole("row")
|
||||
|
||||
expect(within(rows[1]).getByText("0")).toBeTruthy()
|
||||
expect(within(rows[3]).getAllByText("1")[0]).toBeTruthy()
|
||||
|
||||
fireEvent.click(getByText("Number"))
|
||||
|
||||
const rows_updated = getAllByRole("row")
|
||||
expect(within(rows_updated[1]).getAllByText("1")[0]).toBeTruthy()
|
||||
expect(within(rows_updated[3]).getByText("0")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("Sort TrialTable by value", () => {
|
||||
const { getAllByRole, getByText } = render(
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider>
|
||||
<TrialTable studyDetail={studyDetail} isBeta={false} />
|
||||
</SnackbarProvider>
|
||||
</RecoilRoot>
|
||||
)
|
||||
fireEvent.click(getByText("Value"))
|
||||
const rows = getAllByRole("row")
|
||||
expect(within(rows[1]).getByText("-2")).toBeTruthy()
|
||||
expect(within(rows[3]).getByText("-1")).toBeTruthy()
|
||||
|
||||
fireEvent.click(getByText("Value"))
|
||||
const rows_updated = getAllByRole("row")
|
||||
expect(within(rows_updated[1]).getByText("-1")).toBeTruthy()
|
||||
expect(within(rows_updated[3]).getByText("-2")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("Sort TrialTable by duration", () => {
|
||||
const { getAllByRole, getByText } = render(
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider>
|
||||
<TrialTable studyDetail={studyDetail} isBeta={false} />
|
||||
</SnackbarProvider>
|
||||
</RecoilRoot>
|
||||
)
|
||||
fireEvent.click(getByText("Duration(ms)"))
|
||||
const rows = getAllByRole("row")
|
||||
expect(within(rows[1]).getByText("1000")).toBeTruthy()
|
||||
expect(within(rows[3]).getByText("2000")).toBeTruthy()
|
||||
|
||||
fireEvent.click(getByText("Duration(ms)"))
|
||||
const rows_updated = getAllByRole("row")
|
||||
expect(within(rows_updated[1]).getByText("2000")).toBeTruthy()
|
||||
expect(within(rows_updated[3]).getByText("1000")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("Sort TrialTable by state", () => {
|
||||
const { getAllByRole, getByText } = render(
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider>
|
||||
<TrialTable studyDetail={studyDetail} isBeta={false} />
|
||||
</SnackbarProvider>
|
||||
</RecoilRoot>
|
||||
)
|
||||
fireEvent.click(getByText("State"))
|
||||
const rows = getAllByRole("row")
|
||||
expect(within(rows[1]).getByText("Complete")).toBeTruthy()
|
||||
expect(within(rows[3]).getByText("Fail")).toBeTruthy()
|
||||
|
||||
fireEvent.click(getByText("State"))
|
||||
const rows_updated = getAllByRole("row")
|
||||
expect(within(rows_updated[1]).getByText("Fail")).toBeTruthy()
|
||||
expect(within(rows_updated[3]).getByText("Complete")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("Filter trials by state", () => {
|
||||
const { queryAllByText } = render(
|
||||
<RecoilRoot>
|
||||
<SnackbarProvider>
|
||||
<TrialTable studyDetail={studyDetail} isBeta={false} />
|
||||
</SnackbarProvider>
|
||||
</RecoilRoot>
|
||||
)
|
||||
expect(queryAllByText("Fail").length).toBe(1)
|
||||
|
||||
// Click 'Complete' state
|
||||
const completedRows = queryAllByText("Complete")
|
||||
expect(completedRows.length).toBe(1)
|
||||
fireEvent.click(completedRows[0])
|
||||
|
||||
expect(queryAllByText("Fail").length).toBe(0)
|
||||
})
|
||||
Reference in New Issue
Block a user