|
|
@@ -1,99 +1,117 @@
|
|
|
-import React, { useEffect, useState } from 'react'
|
|
|
-import { get, round } from 'lodash'
|
|
|
-import { makeStyles } from '@material-ui/styles'
|
|
|
-import Table from '@material-ui/core/Table'
|
|
|
-import TableBody from '@material-ui/core/TableBody'
|
|
|
-import TableCell from '@material-ui/core/TableCell'
|
|
|
-import TableContainer from '@material-ui/core/TableContainer'
|
|
|
-import TableHead from '@material-ui/core/TableHead'
|
|
|
-import TablePagination from '@material-ui/core/TablePagination'
|
|
|
-import TableRow from '@material-ui/core/TableRow'
|
|
|
-import TableSortLabel from '@material-ui/core/TableSortLabel'
|
|
|
-import Typography from '@material-ui/core/Typography'
|
|
|
-import AllocationChart from './AllocationChart';
|
|
|
-import { toCurrency } from '../util';
|
|
|
+import React, { useEffect, useState } from "react";
|
|
|
+import { get, round } from "lodash";
|
|
|
+import { makeStyles } from "@material-ui/styles";
|
|
|
+import Table from "@material-ui/core/Table";
|
|
|
+import TableBody from "@material-ui/core/TableBody";
|
|
|
+import TableCell from "@material-ui/core/TableCell";
|
|
|
+import TableContainer from "@material-ui/core/TableContainer";
|
|
|
+import TableHead from "@material-ui/core/TableHead";
|
|
|
+import TablePagination from "@material-ui/core/TablePagination";
|
|
|
+import TableRow from "@material-ui/core/TableRow";
|
|
|
+import TableSortLabel from "@material-ui/core/TableSortLabel";
|
|
|
+import Typography from "@material-ui/core/Typography";
|
|
|
+import AllocationChart from "./AllocationChart";
|
|
|
+import { toCurrency } from "../util";
|
|
|
|
|
|
const useStyles = makeStyles({
|
|
|
noResults: {
|
|
|
padding: 24,
|
|
|
},
|
|
|
-})
|
|
|
+});
|
|
|
|
|
|
function descendingComparator(a, b, orderBy) {
|
|
|
if (get(b, orderBy) < get(a, orderBy)) {
|
|
|
- return -1
|
|
|
+ return -1;
|
|
|
}
|
|
|
if (get(b, orderBy) > get(a, orderBy)) {
|
|
|
- return 1
|
|
|
+ return 1;
|
|
|
}
|
|
|
- return 0
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
function getComparator(order, orderBy) {
|
|
|
- return order === 'desc'
|
|
|
+ return order === "desc"
|
|
|
? (a, b) => descendingComparator(a, b, orderBy)
|
|
|
- : (a, b) => -descendingComparator(a, b, orderBy)
|
|
|
+ : (a, b) => -descendingComparator(a, b, orderBy);
|
|
|
}
|
|
|
|
|
|
function stableSort(array, comparator) {
|
|
|
- const stabilizedThis = array.map((el, index) => [el, index])
|
|
|
+ const stabilizedThis = array.map((el, index) => [el, index]);
|
|
|
stabilizedThis.sort((a, b) => {
|
|
|
- const order = comparator(a[0], b[0])
|
|
|
- if (order !== 0) return order
|
|
|
- return a[1] - b[1]
|
|
|
- })
|
|
|
- return stabilizedThis.map((el) => el[0])
|
|
|
+ const order = comparator(a[0], b[0]);
|
|
|
+ if (order !== 0) return order;
|
|
|
+ return a[1] - b[1];
|
|
|
+ });
|
|
|
+ return stabilizedThis.map((el) => el[0]);
|
|
|
}
|
|
|
|
|
|
const headCells = [
|
|
|
- { id: 'name', numeric: false, label: 'Name', width: 'auto' },
|
|
|
- { id: 'cpuCost', numeric: true, label: 'CPU', width: 90 },
|
|
|
- { id: 'ramCost', numeric: true, label: "RAM", width: 90 },
|
|
|
- { id: 'pvCost', numeric: true, label: 'PV', width: 90 },
|
|
|
- { id: 'totalEfficiency', numeric: true, label: 'Efficiency', width: 90 },
|
|
|
- { id: 'totalCost', numeric: true, label: 'Total cost', width: 90 },
|
|
|
-]
|
|
|
-
|
|
|
-const AllocationReport = ({ allocationData, cumulativeData, totalData, currency }) => {
|
|
|
- const classes = useStyles()
|
|
|
+ { id: "name", numeric: false, label: "Name", width: "auto" },
|
|
|
+ { id: "cpuCost", numeric: true, label: "CPU", width: 90 },
|
|
|
+ { id: "ramCost", numeric: true, label: "RAM", width: 90 },
|
|
|
+ { id: "pvCost", numeric: true, label: "PV", width: 90 },
|
|
|
+ { id: "totalEfficiency", numeric: true, label: "Efficiency", width: 90 },
|
|
|
+ { id: "totalCost", numeric: true, label: "Total cost", width: 90 },
|
|
|
+];
|
|
|
+
|
|
|
+const AllocationReport = ({
|
|
|
+ allocationData,
|
|
|
+ cumulativeData,
|
|
|
+ totalData,
|
|
|
+ currency,
|
|
|
+}) => {
|
|
|
+ const classes = useStyles();
|
|
|
|
|
|
if (allocationData.length === 0) {
|
|
|
- return <Typography variant="body2" className={classes.noResults}>No results</Typography>
|
|
|
+ return (
|
|
|
+ <Typography variant="body2" className={classes.noResults}>
|
|
|
+ No results
|
|
|
+ </Typography>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- const [order, setOrder] = React.useState('desc')
|
|
|
- const [orderBy, setOrderBy] = React.useState('totalCost')
|
|
|
- const [page, setPage] = useState(0)
|
|
|
- const [rowsPerPage, setRowsPerPage] = useState(25)
|
|
|
- const numData = cumulativeData.length
|
|
|
+ const [order, setOrder] = React.useState("desc");
|
|
|
+ const [orderBy, setOrderBy] = React.useState("totalCost");
|
|
|
+ const [page, setPage] = useState(0);
|
|
|
+ const [rowsPerPage, setRowsPerPage] = useState(25);
|
|
|
+ const numData = cumulativeData.length;
|
|
|
|
|
|
useEffect(() => {
|
|
|
- setPage(0)
|
|
|
- }, [numData])
|
|
|
+ setPage(0);
|
|
|
+ }, [numData]);
|
|
|
|
|
|
- const lastPage = Math.floor(numData / rowsPerPage)
|
|
|
+ const lastPage = Math.floor(numData / rowsPerPage);
|
|
|
|
|
|
- const handleChangePage = (event, newPage) => setPage(newPage)
|
|
|
+ const handleChangePage = (event, newPage) => setPage(newPage);
|
|
|
|
|
|
- const handleChangeRowsPerPage = event => {
|
|
|
- setRowsPerPage(parseInt(event.target.value, 10))
|
|
|
- setPage(0)
|
|
|
- }
|
|
|
+ const handleChangeRowsPerPage = (event) => {
|
|
|
+ setRowsPerPage(parseInt(event.target.value, 10));
|
|
|
+ setPage(0);
|
|
|
+ };
|
|
|
|
|
|
- const createSortHandler = (property) => (event) => handleRequestSort(event, property)
|
|
|
+ const createSortHandler = (property) => (event) =>
|
|
|
+ handleRequestSort(event, property);
|
|
|
|
|
|
const handleRequestSort = (event, property) => {
|
|
|
- const isDesc = orderBy === property && order === 'desc'
|
|
|
- setOrder(isDesc ? 'asc' : 'desc')
|
|
|
- setOrderBy(property)
|
|
|
- }
|
|
|
+ const isDesc = orderBy === property && order === "desc";
|
|
|
+ setOrder(isDesc ? "asc" : "desc");
|
|
|
+ setOrderBy(property);
|
|
|
+ };
|
|
|
|
|
|
- const orderedRows = stableSort(cumulativeData, getComparator(order, orderBy))
|
|
|
- const pageRows = orderedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
|
|
|
+ const orderedRows = stableSort(cumulativeData, getComparator(order, orderBy));
|
|
|
+ const pageRows = orderedRows.slice(
|
|
|
+ page * rowsPerPage,
|
|
|
+ page * rowsPerPage + rowsPerPage
|
|
|
+ );
|
|
|
|
|
|
return (
|
|
|
<div id="report">
|
|
|
- <AllocationChart allocationRange={allocationData} currency={currency} n={10} height={300} />
|
|
|
+ <AllocationChart
|
|
|
+ allocationRange={allocationData}
|
|
|
+ currency={currency}
|
|
|
+ n={10}
|
|
|
+ height={300}
|
|
|
+ />
|
|
|
<TableContainer>
|
|
|
<Table>
|
|
|
<TableHead>
|
|
|
@@ -102,13 +120,13 @@ const AllocationReport = ({ allocationData, cumulativeData, totalData, currency
|
|
|
<TableCell
|
|
|
key={cell.id}
|
|
|
colSpan={cell.colspan}
|
|
|
- align={cell.numeric ? 'right' : 'left'}
|
|
|
+ align={cell.numeric ? "right" : "left"}
|
|
|
sortDirection={orderBy === cell.id ? order : false}
|
|
|
style={{ width: cell.width }}
|
|
|
>
|
|
|
<TableSortLabel
|
|
|
active={orderBy === cell.id}
|
|
|
- direction={orderBy === cell.id ? order : 'asc'}
|
|
|
+ direction={orderBy === cell.id ? order : "asc"}
|
|
|
onClick={createSortHandler(cell.id)}
|
|
|
>
|
|
|
{cell.label}
|
|
|
@@ -121,35 +139,42 @@ const AllocationReport = ({ allocationData, cumulativeData, totalData, currency
|
|
|
<TableRow>
|
|
|
{headCells.map((cell) => {
|
|
|
return (
|
|
|
- <TableCell
|
|
|
- key={cell.id}
|
|
|
- colSpan={cell.colspan}
|
|
|
- align={cell.numeric ? 'right' : 'left'}
|
|
|
- style={{ fontWeight: 500 }}
|
|
|
- >
|
|
|
- {cell.numeric
|
|
|
- ? (cell.label === 'Efficiency'
|
|
|
- ? (totalData.totalEfficiency == 1.0 && totalData.cpuReqCoreHrs == 0 && totalData.ramReqByteHrs == 0)
|
|
|
- ? "Inf%"
|
|
|
- : `${round(totalData.totalEfficiency*100, 1)}%`
|
|
|
- : toCurrency(totalData[cell.id], currency))
|
|
|
- : totalData[cell.id]}
|
|
|
- </TableCell>
|
|
|
- )})}
|
|
|
+ <TableCell
|
|
|
+ key={cell.id}
|
|
|
+ colSpan={cell.colspan}
|
|
|
+ align={cell.numeric ? "right" : "left"}
|
|
|
+ style={{ fontWeight: 500 }}
|
|
|
+ >
|
|
|
+ {cell.numeric
|
|
|
+ ? cell.label === "Efficiency"
|
|
|
+ ? totalData.totalEfficiency == 1.0 &&
|
|
|
+ totalData.cpuReqCoreHrs == 0 &&
|
|
|
+ totalData.ramReqByteHrs == 0
|
|
|
+ ? "Inf%"
|
|
|
+ : `${round(totalData.totalEfficiency * 100, 1)}%`
|
|
|
+ : toCurrency(totalData[cell.id], currency)
|
|
|
+ : totalData[cell.id]}
|
|
|
+ </TableCell>
|
|
|
+ );
|
|
|
+ })}
|
|
|
</TableRow>
|
|
|
{pageRows.map((row, key) => {
|
|
|
if (row.name === "__unmounted__") {
|
|
|
- row.name = "Unmounted PVs"
|
|
|
+ row.name = "Unmounted PVs";
|
|
|
}
|
|
|
|
|
|
- let isIdle = row.name.indexOf("__idle__") >= 0
|
|
|
- let isUnallocated = row.name.indexOf("__unallocated__") >= 0
|
|
|
- let isUnmounted = row.name.indexOf("Unmounted PVs") >= 0
|
|
|
+ let isIdle = row.name.indexOf("__idle__") >= 0;
|
|
|
+ let isUnallocated = row.name.indexOf("__unallocated__") >= 0;
|
|
|
+ let isUnmounted = row.name.indexOf("Unmounted PVs") >= 0;
|
|
|
|
|
|
// Replace "efficiency" with Inf if there is usage w/o request
|
|
|
- let efficiency = round(row.totalEfficiency*100, 1)
|
|
|
- if (row.totalEfficiency == 1.0 && row.cpuReqCoreHrs == 0 && row.ramReqByteHrs == 0) {
|
|
|
- efficiency = "Inf"
|
|
|
+ let efficiency = round(row.totalEfficiency * 100, 1);
|
|
|
+ if (
|
|
|
+ row.totalEfficiency == 1.0 &&
|
|
|
+ row.cpuReqCoreHrs == 0 &&
|
|
|
+ row.ramReqByteHrs == 0
|
|
|
+ ) {
|
|
|
+ efficiency = "Inf";
|
|
|
}
|
|
|
|
|
|
// Do not allow drill-down for idle and unallocated rows
|
|
|
@@ -157,29 +182,45 @@ const AllocationReport = ({ allocationData, cumulativeData, totalData, currency
|
|
|
return (
|
|
|
<TableRow key={key}>
|
|
|
<TableCell align="left">{row.name}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.cpuCost, currency)}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.ramCost, currency)}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.pvCost, currency)}</TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.cpuCost, currency)}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.ramCost, currency)}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.pvCost, currency)}
|
|
|
+ </TableCell>
|
|
|
{isIdle ? (
|
|
|
<TableCell align="right">—</TableCell>
|
|
|
) : (
|
|
|
<TableCell align="right">{efficiency}%</TableCell>
|
|
|
)}
|
|
|
- <TableCell align="right">{toCurrency(row.totalCost, currency)}</TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.totalCost, currency)}
|
|
|
+ </TableCell>
|
|
|
</TableRow>
|
|
|
- )
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
<TableRow key={key}>
|
|
|
<TableCell align="left">{row.name}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.cpuCost, currency)}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.ramCost, currency)}</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.pvCost, currency)}</TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.cpuCost, currency)}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.ramCost, currency)}
|
|
|
+ </TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.pvCost, currency)}
|
|
|
+ </TableCell>
|
|
|
<TableCell align="right">{efficiency}%</TableCell>
|
|
|
- <TableCell align="right">{toCurrency(row.totalCost, currency)}</TableCell>
|
|
|
+ <TableCell align="right">
|
|
|
+ {toCurrency(row.totalCost, currency)}
|
|
|
+ </TableCell>
|
|
|
</TableRow>
|
|
|
- )
|
|
|
+ );
|
|
|
})}
|
|
|
</TableBody>
|
|
|
</Table>
|
|
|
@@ -194,7 +235,7 @@ const AllocationReport = ({ allocationData, cumulativeData, totalData, currency
|
|
|
onChangeRowsPerPage={handleChangeRowsPerPage}
|
|
|
/>
|
|
|
</div>
|
|
|
- )
|
|
|
-}
|
|
|
+ );
|
|
|
+};
|
|
|
|
|
|
-export default React.memo(AllocationReport)
|
|
|
+export default React.memo(AllocationReport);
|