Added a lot of improvements

This commit is contained in:
c-bata
2022-12-31 16:15:39 +09:00
parent 838299c19c
commit cb891c129b
8 changed files with 219 additions and 75 deletions
+5 -1
View File
@@ -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,
+14 -1
View File
@@ -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"}
/>
}
/>
+3 -2
View File
@@ -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>
+1 -1
View File
@@ -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[]