mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-23 13:30:25 +08:00
Added a lot of improvements
This commit is contained in:
@@ -40,8 +40,8 @@ interface StudyDetailResponse {
|
||||
name: string
|
||||
datetime_start: string
|
||||
directions: StudyDirection[]
|
||||
best_trial?: TrialResponse
|
||||
trials: TrialResponse[]
|
||||
best_trials: TrialResponse[]
|
||||
intersection_search_space: SearchSpace[]
|
||||
union_search_space: SearchSpace[]
|
||||
union_user_attrs: AttributeSpec[]
|
||||
@@ -66,12 +66,16 @@ export const getStudyDetailAPI = (
|
||||
const trials = res.data.trials.map((trial): Trial => {
|
||||
return convertTrialResponse(trial)
|
||||
})
|
||||
const best_trials = res.data.best_trials.map((trial): Trial => {
|
||||
return convertTrialResponse(trial)
|
||||
})
|
||||
return {
|
||||
id: studyId,
|
||||
name: res.data.name,
|
||||
datetime_start: new Date(res.data.datetime_start),
|
||||
directions: res.data.directions,
|
||||
trials: trials,
|
||||
best_trials: best_trials,
|
||||
union_search_space: res.data.union_search_space,
|
||||
intersection_search_space: res.data.intersection_search_space,
|
||||
union_user_attrs: res.data.union_user_attrs,
|
||||
|
||||
@@ -2,6 +2,8 @@ import React, { FC, useMemo, useState, useEffect } from "react"
|
||||
import { RecoilRoot } from "recoil"
|
||||
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
|
||||
import { SnackbarProvider } from "notistack"
|
||||
import blue from "@mui/material/colors/blue"
|
||||
import pink from "@mui/material/colors/pink"
|
||||
import {
|
||||
createTheme,
|
||||
useMediaQuery,
|
||||
@@ -25,6 +27,8 @@ export const App: FC = () => {
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: colorMode,
|
||||
primary: blue,
|
||||
secondary: pink,
|
||||
},
|
||||
}),
|
||||
[colorMode]
|
||||
@@ -53,7 +57,16 @@ export const App: FC = () => {
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"top"}
|
||||
page={"history"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={URL_PREFIX + "/studies/:studyId/analytics"}
|
||||
children={
|
||||
<StudyDetailBeta
|
||||
toggleColorMode={toggleColorMode}
|
||||
page={"analytics"}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,8 @@ import { actionCreator } from "../action"
|
||||
export const Note: FC<{
|
||||
studyId: number
|
||||
latestNote: Note
|
||||
}> = ({ studyId, latestNote }) => {
|
||||
minRows: number
|
||||
}> = ({ studyId, latestNote, minRows }) => {
|
||||
const theme = useTheme()
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [edited, setEdited] = useState(false)
|
||||
@@ -72,7 +73,7 @@ export const Note: FC<{
|
||||
</Typography>
|
||||
<TextField
|
||||
disabled={saving}
|
||||
minRows={5}
|
||||
minRows={minRows}
|
||||
multiline={true}
|
||||
placeholder="Description about the study... (This note is saved to study's system_attrs)"
|
||||
sx={{ width: "100%", margin: `${theme.spacing(1)} 0` }}
|
||||
|
||||
@@ -235,7 +235,11 @@ export const StudyDetail: FC<{
|
||||
<TrialTable studyDetail={studyDetail} />
|
||||
</Card>
|
||||
{studyDetail !== null ? (
|
||||
<Note studyId={studyIdNumber} latestNote={studyDetail.note} />
|
||||
<Note
|
||||
studyId={studyIdNumber}
|
||||
latestNote={studyDetail.note}
|
||||
minRows={5}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -1,47 +1,50 @@
|
||||
import React, { FC, useEffect } from "react"
|
||||
import { useRecoilState, useRecoilValue } from "recoil"
|
||||
import { Link, useParams } from "react-router-dom"
|
||||
import MuiDrawer from "@mui/material/Drawer"
|
||||
import IconButton from "@mui/material/IconButton"
|
||||
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"
|
||||
import Divider from "@mui/material/Divider"
|
||||
import List from "@mui/material/List"
|
||||
import ListItem from "@mui/material/ListItem"
|
||||
import ListItemButton from "@mui/material/ListItemButton"
|
||||
import ListItemIcon from "@mui/material/ListItemIcon"
|
||||
import ListItemText from "@mui/material/ListItemText"
|
||||
import ListSubheader from "@mui/material/ListSubheader"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { useParams } from "react-router-dom"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
Box,
|
||||
useTheme,
|
||||
Switch,
|
||||
Theme,
|
||||
CSSObject,
|
||||
styled,
|
||||
Typography,
|
||||
Toolbar,
|
||||
useTheme,
|
||||
ListItem,
|
||||
} from "@mui/material"
|
||||
import Grid2 from "@mui/material/Unstable_Grid2"
|
||||
|
||||
import { GraphHistory } from "./GraphHistory"
|
||||
import { Note } from "./Note"
|
||||
import { actionCreator } from "../action"
|
||||
import { reloadIntervalState, studyDetailsState } from "../state"
|
||||
import {
|
||||
reloadIntervalState,
|
||||
studyDetailsState,
|
||||
studySummariesState,
|
||||
} from "../state"
|
||||
import { TrialTable } from "./TrialTable"
|
||||
import { StudyDetailDrawer } from "./StudyDetailDrawer"
|
||||
import { GraphParallelCoordinate } from "./GraphParallelCoordinate"
|
||||
import { Contour } from "./GraphContour"
|
||||
import { GraphHyperparameterImportances } from "./GraphHyperparameterImportances"
|
||||
import { GraphSlice } from "./GraphSlice"
|
||||
import { GraphParetoFront } from "./GraphParetoFront"
|
||||
import { DataGrid, DataGridColumn } from "./DataGrid"
|
||||
import List from "@mui/material/List"
|
||||
|
||||
interface ParamTypes {
|
||||
studyId: string
|
||||
}
|
||||
|
||||
type PageId = "top" | "trials" | "note"
|
||||
type PageId = "history" | "analytics" | "trials" | "note"
|
||||
|
||||
const useStudyDetailValue = (studyId: number): StudyDetail | null => {
|
||||
const studyDetails = useRecoilValue<StudyDetails>(studyDetailsState)
|
||||
return studyDetails[studyId] || null
|
||||
}
|
||||
|
||||
const useStudySummaryValue = (studyId: number): StudySummary | null => {
|
||||
const studySummaries = useRecoilValue<StudySummary[]>(studySummariesState)
|
||||
return studySummaries.find((s) => s.study_id == studyId) || null
|
||||
}
|
||||
|
||||
export const StudyDetailBeta: FC<{
|
||||
toggleColorMode: () => void
|
||||
page: PageId
|
||||
@@ -52,71 +55,169 @@ export const StudyDetailBeta: FC<{
|
||||
const studyIdNumber = parseInt(studyId, 10)
|
||||
const studyDetail = useStudyDetailValue(studyIdNumber)
|
||||
const reloadInterval = useRecoilValue<number>(reloadIntervalState)
|
||||
const studySummary = useStudySummaryValue(studyIdNumber)
|
||||
const directions = studyDetail?.directions || studySummary?.directions || null
|
||||
const userAttrs = studySummary?.user_attrs || []
|
||||
|
||||
const title =
|
||||
studyDetail !== null || studySummary !== null
|
||||
? `${studyDetail?.name || studySummary?.study_name} (id=${studyId})`
|
||||
: `Study #${studyId}`
|
||||
|
||||
useEffect(() => {
|
||||
action.updateStudyDetail(studyIdNumber)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (reloadInterval < 0) {
|
||||
if (reloadInterval < 0 || page === "trials") {
|
||||
return
|
||||
}
|
||||
const intervalId = setInterval(function () {
|
||||
action.updateStudyDetail(studyIdNumber)
|
||||
}, reloadInterval * 1000)
|
||||
return () => clearInterval(intervalId)
|
||||
}, [reloadInterval, studyDetail])
|
||||
}, [reloadInterval, studyDetail, page])
|
||||
|
||||
const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
|
||||
|
||||
const trialListWidth = 240
|
||||
const userAttrColumns: DataGridColumn<Attribute>[] = [
|
||||
{ field: "key", label: "Key", sortable: true },
|
||||
{ field: "value", label: "Value", sortable: true },
|
||||
]
|
||||
|
||||
let content = null
|
||||
if (page === "top") {
|
||||
if (page === "history") {
|
||||
content = (
|
||||
<Box sx={{ display: "flex", width: "100%" }}>
|
||||
<Box sx={{ height: "100vh", width: trialListWidth, overflow: "auto" }}>
|
||||
<List dense={true}>
|
||||
<ListSubheader>{`Trials (${
|
||||
studyDetail?.trials.length || 0
|
||||
})`}</ListSubheader>
|
||||
{trials.map((trial, i) => {
|
||||
return (
|
||||
<ListItem key={trial.trial_id} disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemText
|
||||
primary={`Trial ${trial.trial_id}`}
|
||||
secondary={`State=${trial.state}`}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
<Divider orientation="vertical" flexItem />
|
||||
<Box
|
||||
component="main"
|
||||
sx={{ flexGrow: 1, bgcolor: "background.default", p: 3 }}
|
||||
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
|
||||
<Card
|
||||
sx={{
|
||||
margin: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
margin: theme.spacing(2),
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<GraphHistory study={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
{directions !== null && directions.length > 1 ? (
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<GraphHistory study={studyDetail} />
|
||||
<GraphParetoFront study={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
) : null}
|
||||
<Grid2 container spacing={2}>
|
||||
<Grid2 xs={6}>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
{studyDetail !== null &&
|
||||
studyDetail.best_trials.length === 1 && (
|
||||
<>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ margin: "1em 0", fontWeight: 600 }}
|
||||
>
|
||||
Best Trial
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{ fontWeight: 600 }}
|
||||
color="secondary"
|
||||
>
|
||||
{studyDetail.best_trials[0].values}
|
||||
</Typography>
|
||||
<List>
|
||||
{studyDetail.best_trials[0].params.map((param) => (
|
||||
<ListItem>
|
||||
{param.name} {param.value}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
{studyDetail !== null && studyDetail.best_trials.length > 1 && (
|
||||
<>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ margin: "1em 0", fontWeight: 600 }}
|
||||
>
|
||||
Best Trials
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid2>
|
||||
<Grid2 xs={6}>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ margin: "1em 0", fontWeight: 600 }}
|
||||
>
|
||||
Study User Attributes
|
||||
</Typography>
|
||||
<DataGrid<Attribute>
|
||||
columns={userAttrColumns}
|
||||
rows={userAttrs}
|
||||
keyField={"key"}
|
||||
dense={true}
|
||||
initialRowsPerPage={5}
|
||||
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid2>
|
||||
</Grid2>
|
||||
</Box>
|
||||
)
|
||||
} else if (page === "analytics") {
|
||||
content = (
|
||||
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
|
||||
<Typography variant="h5" sx={{ margin: theme.spacing(2) }}>
|
||||
Hyperparameter Importance
|
||||
</Typography>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<GraphHyperparameterImportances
|
||||
study={studyDetail}
|
||||
studyId={studyIdNumber}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Typography variant="h5" sx={{ margin: theme.spacing(2) }}>
|
||||
Hyperparameter Relationships
|
||||
</Typography>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<GraphSlice study={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<GraphParallelCoordinate study={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<Contour study={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
)
|
||||
} else if (page === "trials") {
|
||||
content = <TrialTable studyDetail={studyDetail} />
|
||||
content = (
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
<TrialTable studyDetail={studyDetail} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
} else {
|
||||
content =
|
||||
studyDetail !== null ? (
|
||||
<Note studyId={studyIdNumber} latestNote={studyDetail.note} />
|
||||
<Note
|
||||
studyId={studyIdNumber}
|
||||
latestNote={studyDetail.note}
|
||||
minRows={30}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -126,6 +227,7 @@ export const StudyDetailBeta: FC<{
|
||||
studyId={studyIdNumber}
|
||||
page={page}
|
||||
toggleColorMode={toggleColorMode}
|
||||
title={title}
|
||||
>
|
||||
{content}
|
||||
</StudyDetailDrawer>
|
||||
|
||||
@@ -27,8 +27,9 @@ import TableViewIcon from "@mui/icons-material/TableView"
|
||||
import RateReviewIcon from "@mui/icons-material/RateReview"
|
||||
import ClearIcon from "@mui/icons-material/Clear"
|
||||
import MenuIcon from "@mui/icons-material/Menu"
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||
import GitHubIcon from "@mui/icons-material/GitHub"
|
||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew"
|
||||
import QueryStatsIcon from "@mui/icons-material/QueryStats"
|
||||
import { Switch } from "@mui/material"
|
||||
|
||||
const drawerWidth = 240
|
||||
@@ -105,11 +106,12 @@ const Drawer = styled(MuiDrawer, {
|
||||
export const StudyDetailDrawer: FC<{
|
||||
studyId: number
|
||||
toggleColorMode: () => void
|
||||
page: "top" | "trials" | "note"
|
||||
page: "history" | "analytics" | "trials" | "note"
|
||||
title: string
|
||||
children?: React.ReactNode
|
||||
}> = ({ studyId, toggleColorMode, page, children }) => {
|
||||
}> = ({ studyId, toggleColorMode, page, title, children }) => {
|
||||
const theme = useTheme()
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const [open, setOpen] = React.useState(true)
|
||||
const [reloadInterval, updateReloadInterval] =
|
||||
useRecoilState<number>(reloadIntervalState)
|
||||
|
||||
@@ -157,8 +159,8 @@ export const StudyDetailDrawer: FC<{
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
Mini variant drawer
|
||||
<Typography variant="h5" noWrap component="div">
|
||||
{title}
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
@@ -174,12 +176,12 @@ export const StudyDetailDrawer: FC<{
|
||||
</DrawerHeader>
|
||||
<Divider />
|
||||
<List>
|
||||
<ListItem key="Top" disablePadding sx={styleListItem}>
|
||||
<ListItem key="History" disablePadding sx={styleListItem}>
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
to={`${URL_PREFIX}/studies/${studyId}/beta`}
|
||||
sx={styleListItemButton}
|
||||
selected={page === "top"}
|
||||
selected={page === "history"}
|
||||
>
|
||||
<ListItemIcon sx={styleListItemIcon}>
|
||||
<AutoGraphIcon />
|
||||
@@ -187,6 +189,19 @@ export const StudyDetailDrawer: FC<{
|
||||
<ListItemText primary="History" sx={styleListItemText} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem key="Analytics" disablePadding sx={styleListItem}>
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
to={`${URL_PREFIX}/studies/${studyId}/analytics`}
|
||||
sx={styleListItemButton}
|
||||
selected={page === "analytics"}
|
||||
>
|
||||
<ListItemIcon sx={styleListItemIcon}>
|
||||
<QueryStatsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Analytics" sx={styleListItemText} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem key="Table" disablePadding sx={styleListItem}>
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
@@ -278,7 +293,11 @@ export const StudyDetailDrawer: FC<{
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem key="Feedback" disablePadding sx={styleListItem}>
|
||||
<ListItemButton target="_blank" href="https://github.com/optuna/optuna-dashboard/issues/new/choose" sx={styleListItemButton}>
|
||||
<ListItemButton
|
||||
target="_blank"
|
||||
href="https://github.com/optuna/optuna-dashboard/issues/new/choose"
|
||||
sx={styleListItemButton}
|
||||
>
|
||||
<ListItemIcon sx={styleListItemIcon}>
|
||||
<GitHubIcon />
|
||||
</ListItemIcon>
|
||||
|
||||
@@ -376,6 +376,7 @@ export const StudyList: FC<{
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
{sortBySelect}
|
||||
</Box>
|
||||
</CardContent>
|
||||
|
||||
Vendored
+1
-1
@@ -96,7 +96,7 @@ declare interface StudyDetail {
|
||||
name: string
|
||||
directions: StudyDirection[]
|
||||
datetime_start: Date
|
||||
best_trial?: Trial
|
||||
best_trials: Trial[]
|
||||
trials: Trial[]
|
||||
intersection_search_space: SearchSpace[]
|
||||
union_search_space: SearchSpace[]
|
||||
|
||||
Reference in New Issue
Block a user