From 6bec4f1f9fdc3785e4ec7339833d0f1e92cd1d86 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Wed, 17 Apr 2024 21:32:12 +0900 Subject: [PATCH] Delete unnecessary code --- optuna_dashboard/ts/components/DataGrid.tsx | 426 +----------------- optuna_dashboard/ts/components/TrialTable.tsx | 156 +------ 2 files changed, 13 insertions(+), 569 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 8b43ae59..051816ad 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -9,7 +9,6 @@ import { TableRow, TableSortLabel, TextField, - Collapse, IconButton, Menu, MenuItem, @@ -17,8 +16,6 @@ import { useTheme, } from "@mui/material" import { styled } from "@mui/system" -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown" -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp" import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank" import CheckBoxIcon from "@mui/icons-material/CheckBox" import FilterListIcon from "@mui/icons-material/FilterList" @@ -45,224 +42,6 @@ import { useReactTable, } from "@tanstack/react-table" -type Order = "asc" | "desc" - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Value = any - -const defaultRowsPerPageOption = [10, 50, 100, { label: "All", value: -1 }] - -interface DataGridColumn { - field: keyof T - label: string - sortable?: boolean - less?: (a: T, b: T, ascending: boolean) => number - filterChoices?: (string | null)[] - toCellValue?: (rowIndex: number) => string | React.ReactNode - padding?: "normal" | "checkbox" | "none" -} - -interface RowFilter { - columnIdx: number - values: Value[] -} - -function DataGrid(props: { - columns: DataGridColumn[] - rows: T[] - keyField: keyof T - dense?: boolean - collapseBody?: (rowIndex: number) => React.ReactNode - initialRowsPerPage?: number - rowsPerPageOption?: Array - defaultFilter?: (row: T) => boolean -}): React.ReactElement { - const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props - let { initialRowsPerPage, rowsPerPageOption } = props - const [order, setOrder] = React.useState("asc") - const [orderBy, setOrderBy] = React.useState(0) // index of columns - const [page, setPage] = React.useState(0) - const [filters, setFilters] = React.useState([]) - - const getRowIndex = (row: T): number => { - return rows.findIndex((row2) => row[keyField] === row2[keyField]) - } - - // Pagination - rowsPerPageOption = rowsPerPageOption || defaultRowsPerPageOption - initialRowsPerPage = initialRowsPerPage // use first element as default - ? initialRowsPerPage - : isNumber(rowsPerPageOption[0]) - ? rowsPerPageOption[0] - : rowsPerPageOption[0].value - const [rowsPerPage, setRowsPerPage] = React.useState(initialRowsPerPage) - - const handleChangePage = (event: unknown, newPage: number) => { - setPage(newPage) - } - - const handleChangeRowsPerPage = ( - event: React.ChangeEvent - ) => { - setRowsPerPage(parseInt(event.target.value, 10)) - setPage(0) - } - - const PaginationForm: React.FC<{ - onPageNumberSubmit: (value: number) => void - maxPageNumber: number - }> = ({ onPageNumberSubmit, maxPageNumber }) => { - // This component is separated from DataGrid to prevent `DataGrid` from re-rendering the page, - // every time any letters are input. - const [specifiedPageText, setSpecifiedPageText] = React.useState("") - - const handleSubmitPageNumber = ( - event: React.FormEvent - ) => { - event.preventDefault() - const newPageNumber = parseInt(specifiedPageText, 10) - // Page is 0-indexed in `TablePagination`. - onPageNumberSubmit(newPageNumber - 1) - setSpecifiedPageText("") // reset the input field - } - - return ( -
- { - setSpecifiedPageText(e.target.value) - }} - /> - - ) - } - - // Filtering - const filteredRows = rows.filter((row, rowIdx) => { - if (defaultFilter !== undefined && defaultFilter(row)) { - return false - } - return filters.length === 0 - ? true - : filters.every((f) => { - if (columns.length <= f.columnIdx) { - console.log( - `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}` - ) - return true - } - const toCellValue = columns[f.columnIdx].toCellValue - const cellValue = - toCellValue !== undefined - ? toCellValue(rowIdx) - : row[columns[f.columnIdx].field] - return f.values.some((v) => v === cellValue) - }) - }) - - // Sorting - const sortedRows = stableSort(filteredRows, order, orderBy, columns) - const currentPageRows = - rowsPerPage > 0 - ? sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - : sortedRows - const emptyRows = - rowsPerPage - Math.min(rowsPerPage, sortedRows.length - page * rowsPerPage) - - const RootDiv = styled("div")({ - width: "100%", - }) - const maxPageNumber = Math.ceil(filteredRows.length / rowsPerPage) - return ( - - - - - - {collapseBody ? : null} - {columns.map((column, columnIdx) => { - return ( - - key={columnIdx} - column={column} - order={orderBy === columnIdx ? order : null} - filter={ - filters.find((f) => f.columnIdx === columnIdx) || null - } - onOrderByChange={(direction: Order) => { - setOrder(direction) - setOrderBy(columnIdx) - }} - onFilterChange={(values: Value[]) => { - const newFilters = filters.filter( - (f) => f.columnIdx !== columnIdx - ) - newFilters.push({ - columnIdx: columnIdx, - values: values, - }) - setFilters(newFilters) - }} - /> - ) - })} - - - - {currentPageRows.map((row) => ( - - columns={columns} - rowIndex={getRowIndex(row)} - row={row} - keyField={keyField} - collapseBody={collapseBody} - key={`${row[keyField]}`} - /> - ))} - {emptyRows > 0 && ( - - - - )} - -
-
- {filteredRows.length > 0 ? ( - <> - {/* @ts-ignore */} - - - {maxPageNumber > 2 ? ( - setPage(page)} - maxPageNumber={maxPageNumber} - /> - ) : null} - - - ) : null} -
- ) -} - const TableHeaderCellSpan = styled("span")({ display: "inline-flex", }) @@ -279,205 +58,7 @@ const HiddenSpan = styled("span")({ width: 1, }) -function DataGridHeaderColumn(props: { - column: DataGridColumn - order: Order | null - onOrderByChange: (order: Order) => void - filter: RowFilter | null - onFilterChange: (values: Value[]) => void - dense?: boolean -}) { - const { column, order, onOrderByChange, filter, onFilterChange, dense } = - props - const [filterMenuAnchorEl, setFilterMenuAnchorEl] = - React.useState(null) - - const filterChoices = column.filterChoices - - return ( - - - {column.sortable ? ( - { - onOrderByChange(order === "asc" ? "desc" : "asc") - }} - > - {column.label} - {order !== null ? ( - - {order === "desc" ? "sorted descending" : "sorted ascending"} - - ) : null} - - ) : ( - column.label - )} - {filterChoices !== undefined ? ( - <> - { - setFilterMenuAnchorEl(e.currentTarget) - }} - > - - - { - setFilterMenuAnchorEl(null) - }} - > - {filterChoices.map((choice) => ( - { - const newTickedValues = - filter === null - ? filterChoices.filter((v) => v !== choice) // By default, every choice is ticked, so the chosen option will be unticked. - : filter.values.some((v) => v === choice) - ? filter.values.filter((v) => v !== choice) - : [...filter.values, choice] - onFilterChange(newTickedValues) - }} - > - - {!filter || filter.values.some((v) => v === choice) ? ( - - ) : ( - - )} - - {choice ?? "(missing value)"} - - ))} - - - ) : null} - - - ) -} - -function DataGridRow(props: { - columns: DataGridColumn[] - rowIndex: number - row: T - keyField: keyof T - collapseBody?: (rowIndex: number) => React.ReactNode -}) { - const { columns, rowIndex, row, keyField, collapseBody } = props - const [open, setOpen] = React.useState(false) - - return ( - - - {collapseBody ? ( - - setOpen(!open)} - > - {open ? : } - - - ) : null} - {columns.map((column, columnIndex) => { - const cellItem = column.toCellValue - ? column.toCellValue(rowIndex) - : // TODO(c-bata): Avoid this implicit type conversion. - (row[column.field] as number | string | null | undefined) - - return ( - - {cellItem} - - ) - })} - - {collapseBody ? ( - - - - {collapseBody(rowIndex)} - - - - ) : null} - - ) -} - -function getComparator( - order: Order, - columns: DataGridColumn[], - orderBy: number -): (a: T, b: T) => number { - return order === "desc" - ? (a, b) => descendingComparator(a, b, columns, orderBy) - : (a, b) => -descendingComparator(a, b, columns, orderBy) -} - -function descendingComparator( - a: T, - b: T, - columns: DataGridColumn[], - orderBy: number -): number { - const field = columns[orderBy].field - if (b[field] < a[field]) { - return -1 - } - if (b[field] > a[field]) { - return 1 - } - return 0 -} - -function stableSort( - array: T[], - order: Order, - orderBy: number, - columns: DataGridColumn[] -) { - // 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) => { - if (less) { - const ascending = order === "asc" - const result = ascending - ? -less(a[0], b[0], ascending) - : less(a[0], b[0], ascending) - 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]) -} - -const isNumber = ( - rowsPerPage: number | { value: number; label: string } -): rowsPerPage is number => { - return typeof rowsPerPage === "number" -} - -function DataGrid2(props: { +function DataGrid(props: { data: Trial[] columns: ColumnDef[] }): React.ReactElement { @@ -533,7 +114,8 @@ function DataGrid2(props: { const filterChoices = header.column.getCanFilter() ? Array.from( header.column.getFacetedUniqueValues().keys() - ).sort() : null + ).sort() + : null return ( {header.isPlaceholder ? null : ( @@ -784,4 +366,4 @@ const PaginationForm1: React.FC<{ ) } -export { DataGrid, DataGrid2, DataGridColumn } +export { DataGrid } diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index 1ff6eb46..26922ea0 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -3,7 +3,7 @@ import { IconButton, Button, useTheme } from "@mui/material" import LinkIcon from "@mui/icons-material/Link" import DownloadIcon from "@mui/icons-material/Download" -import { DataGridColumn, DataGrid, DataGrid2 } from "./DataGrid" +import { DataGrid } from "./DataGrid" import { Link } from "react-router-dom" import { @@ -29,22 +29,9 @@ export const TrialTable: FC<{ }> = ({ studyDetail, initialRowsPerPage }) => { const theme = useTheme() const trials: Trial[] = studyDetail !== null ? studyDetail.trials : [] - const objectiveNames: string[] = studyDetail?.objective_names || [] - - const columns: DataGridColumn[] = [ - { field: "number", label: "Number", sortable: true, padding: "none" }, - { - field: "state", - label: "State", - sortable: true, - filterChoices: ["Complete", "Pruned", "Fail", "Running", "Waiting"], - padding: "none", - toCellValue: (i) => trials[i].state.toString(), - }, - ] const columnHelper = createColumnHelper() - const tcolumns: ColumnDef[] = [ + const columns: ColumnDef[] = [ columnHelper.accessor("number", { header: "Number", footer: (info) => info.column.id, @@ -58,46 +45,9 @@ export const TrialTable: FC<{ filterFn: multiValueFilter, }), ] - const valueComparator = ( - firstVal?: TrialValueNumber, - secondVal?: TrialValueNumber, - ascending: boolean = true - ): number => { - if (firstVal === secondVal) { - return 0 - } - if (firstVal === undefined) { - return ascending ? -1 : 1 - } else if (secondVal === undefined) { - return ascending ? 1 : -1 - } - if (firstVal === "-inf" || secondVal === "inf") { - return 1 - } else if (secondVal === "-inf" || firstVal === "inf") { - return -1 - } - return firstVal < secondVal ? 1 : -1 - } + if (studyDetail === null || studyDetail.directions.length === 1) { - columns.push({ - field: "values", - label: "Value", - sortable: true, - less: (firstEl, secondEl, ascending): number => { - return valueComparator( - firstEl.values?.[0], - secondEl.values?.[0], - ascending - ) - }, - toCellValue: (i) => { - if (trials[i].values === undefined) { - return null - } - return trials[i].values?.[0] - }, - }) - tcolumns.push( + columns.push( columnHelper.accessor("values", { header: "Value", footer: (info) => info.column.id, @@ -107,30 +57,7 @@ export const TrialTable: FC<{ }) ) } else { - const objectiveColumns: DataGridColumn[] = - studyDetail.directions.map((s, objectiveId) => ({ - field: "values", - label: - objectiveNames.length === studyDetail?.directions.length - ? objectiveNames[objectiveId] - : `Objective ${objectiveId}`, - sortable: true, - less: (firstEl, secondEl, ascending): number => { - return valueComparator( - firstEl.values?.[objectiveId], - secondEl.values?.[objectiveId], - ascending - ) - }, - toCellValue: (i) => { - if (trials[i].values === undefined) { - return null - } - return trials[i].values?.[objectiveId] - }, - })) - columns.push(...objectiveColumns) - tcolumns.push( + columns.push( ...studyDetail.directions.map((s, objectiveId) => columnHelper.accessor((row) => row["values"]?.[objectiveId], { id: `values_${objectiveId}`, @@ -158,26 +85,7 @@ export const TrialTable: FC<{ if (filterChoices !== undefined && isDynamicSpace && hasMissingValue) { filterChoices.push(null) } - columns.push({ - field: "params", - label: `Param ${s.name}`, - toCellValue: (i) => - trials[i].params.find((p) => p.name === s.name)?.param_external_value || - null, - sortable: sortable, - filterChoices: filterChoices, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - less: (firstEl, secondEl, _): number => { - const firstVal = firstEl.params.find( - (p) => p.name === s.name - )?.param_internal_value - const secondVal = secondEl.params.find( - (p) => p.name === s.name - )?.param_internal_value - return valueComparator(firstVal, secondVal) - }, - }) - tcolumns.push( + columns.push( columnHelper.accessor( (row) => row["params"].find((p) => p.name === s.name)?.param_external_value || @@ -196,28 +104,7 @@ export const TrialTable: FC<{ }) studyDetail?.union_user_attrs.forEach((attr_spec) => { - columns.push({ - field: "user_attrs", - label: `UserAttribute ${attr_spec.key}`, - toCellValue: (i) => - trials[i].user_attrs.find((attr) => attr.key === attr_spec.key) - ?.value || null, - sortable: attr_spec.sortable, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - less: (firstEl, secondEl, _): number => { - const firstValString = firstEl.user_attrs.find( - (attr) => attr.key === attr_spec.key - )?.value - const secondValString = secondEl.user_attrs.find( - (attr) => attr.key === attr_spec.key - )?.value - return valueComparator( - Number(firstValString) ?? firstValString, - Number(secondValString) ?? secondValString - ) - }, - }) - tcolumns.push( + columns.push( columnHelper.accessor( (row) => row["user_attrs"].find((a) => a.key === attr_spec.key)?.value || null, @@ -232,25 +119,7 @@ export const TrialTable: FC<{ ) ) }) - columns.push({ - field: "trial_id", - label: "Detail", - toCellValue: (i) => ( - - - - ), - }) - tcolumns.push( + columns.push( columnHelper.accessor((row) => row, { header: "Detail", cell: (info) => ( @@ -277,13 +146,7 @@ export const TrialTable: FC<{ return ( <> - - columns={columns} - rows={trials} - keyField={"trial_id"} - dense={true} - initialRowsPerPage={initialRowsPerPage} - /> + - ) }