This commit is contained in:
Evan Lanham
2022-03-24 00:28:48 -06:00
parent f7e82d1f5a
commit 0659a9736e
30 changed files with 506 additions and 140 deletions
@@ -0,0 +1,9 @@
import React from "react";
import ComparisonPanel from "./ComparisonPanel";
const AnalyticsPanel = (props) => {
if (props.selectedTeams.length > 0) return <ComparisonPanel selectedTeams={props.selectedTeams} />;
return <div />;
};
export default AnalyticsPanel;
@@ -0,0 +1,67 @@
import React from "react";
import { ProcessedDataBucketContext, useProcessedDataBucket } from "../../ProcessedDataBucketContext";
import Chart from "react-apexcharts";
import { Box } from "@mui/material";
const ComparativeBoxPlot = (props) => {
let { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
const getPercentile = (sorted_set, percentile) => {
let idx = percentile * sorted_set.length;
if (Math.floor(idx) == idx) return sorted_set[idx - 1] / 2 + sorted_set[idx] / 2;
else return sorted_set[Math.floor(idx)];
};
const getFiveNumberSummary = (set) => {
set.sort();
return [set[0], getPercentile(set, 0.25), getPercentile(set, 0.5), getPercentile(set, 0.75), set[set.length - 1]];
};
const generateBoxPlotData = (pdb, setName, selectedTeams) => {
let data = [];
for (const teamNumber of selectedTeams) {
//console.log(pdb.teamData[teamNumber].data_sets[setName]);
data.push({
x: teamNumber,
y: getFiveNumberSummary(pdb.teamData[teamNumber].data_sets[setName]),
});
}
return [
{
type: "boxPlot",
data: data,
},
];
};
return (
<Box sx={{ width: "350px", height: "220px" }}>
<Chart
type="boxPlot"
options={{
chart: {
type: "boxPlot",
width: 350,
},
theme: {
mode: "dark",
},
title: {
text: props.title,
},
}}
series={generateBoxPlotData(processedDataBucket, props.setName, props.selectedTeams)}
/>
</Box>
/* <Chart
options={{
chart: {
width: 384,
type: "pie",
},
labels: ["None", "Low", "Mid", "High", "Transversal"],
}}
series={processedDataBucket.teamData[4388].climb_counts}
type="pie"
width={380}
/> */
);
};
export default ComparativeBoxPlot;
@@ -0,0 +1,64 @@
import React from "react";
import { ProcessedDataBucketContext, useProcessedDataBucket } from "../../ProcessedDataBucketContext";
import Chart from "react-apexcharts";
import { Box } from "@mui/material";
import ComparativeBoxPlot from "./ComparativeBoxPlot";
const ComparisonPanel = (props) => {
let { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
if (processedDataBucket == null) return <div />;
//gets the given percentile of a sorted set
return (
<Box
sx={{
height: "400px",
m: 1,
display: "flex",
flexWrap: "wrap",
flexDirection: "row",
justifyContent: "center",
p: 1,
gap: 2,
}}
>
<ComparativeBoxPlot setName="total_match_points" title="Total Match Points" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="auto_points" title="Auto Points" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="teleop_hub_points" title="Teleop Hub Points" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="climb_points" title="Climb Points" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="upper_hub_auto" title="Upper Hub Auto" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="lower_hub_auto" title="Lower Hub Auto" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="upper_hub_teleop" title="Upper Hub Teleop" selectedTeams={props.selectedTeams} />
<ComparativeBoxPlot setName="lower_hub_teleop" title="Lower Hub Teleop" selectedTeams={props.selectedTeams} />
{/* <Box sx={{ width: "300px", height: "200px" }}>
<Chart
type="boxPlot"
options={{
chart: {
type: "boxPlot",
width: 350,
},
theme: {
mode: "dark",
},
}}
series={generateBoxPlotData(processedDataBucket, "upper_hub_auto", props.selectedTeams)}
/>
</Box> */}
{/* <Chart
options={{
chart: {
width: 384,
type: "pie",
},
labels: ["None", "Low", "Mid", "High", "Transversal"],
}}
series={processedDataBucket.teamData[4388].climb_counts}
type="pie"
width={380}
/> */}
</Box>
);
};
export default ComparisonPanel;
@@ -1,9 +1,15 @@
import React from "react";
import React, { useState, useCallback, useRef } from "react";
import { useLocalDb } from "../../DbContext";
import { ProcessedDataBucketContext, useProcessedDataBucket } from "../../ProcessedDataBucketContext";
import Chart from "react-apexcharts";
import { DataGrid } from "@mui/x-data-grid";
import { Box } from "@mui/material";
import AnalyticsPanel from "./AnalyticsPanel";
//https://ag-grid.com/react-data-grid/
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine-dark.css";
const DashboardPage = () => {
// <ProcessedDataBucketContext.Consumer>
@@ -26,72 +32,90 @@ const DashboardPage = () => {
// };
// };
let { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
console.log(processedDataBucket);
if (processedDataBucket == null) {
return <div />;
let rowData = [];
if (processedDataBucket != null) {
//turns the values of the key value pairs in the list into an array
let team_data_array = Object.values(processedDataBucket.teamData);
// let team_data_array = Array.from(processedDataBucket.teamData);
const roundPlaces = (n, d) => Math.round(n * Math.pow(10, d)) / Math.pow(10, d);
// team_data_array.forEach((value, index, array) => {
for (const property in processedDataBucket.teamData) {
let value = processedDataBucket.teamData[property];
rowData.push({
id: value.team_number,
average_auto_points: roundPlaces(value.average_auto_points, 2),
average_teleop_hub_points: roundPlaces(value.average_teleop_hub_points, 2),
average_climb_points: roundPlaces(value.average_climb_points, 2),
average_total_match_points: roundPlaces(value.average_total_match_points, 2),
matches_played: value.matches_played,
num_disables: value.num_disables,
num_flips: value.num_flips,
fouls: value.fouls,
fouls_tech: value.fouls_tech,
red_cards: value.red_cards,
yellow_cards: value.yellow_cards,
});
}
}
const [columnDefs] = useState([
{ field: "id", headerName: "Team", width: 100, checkboxSelection: true, pinned: "left", sortable: true },
{ field: "average_total_match_points", headerName: "Avg Total Pts", width: 120, sortable: true },
{ field: "average_auto_points", headerName: "Avg Auto Pts", width: 120, sortable: true },
{ field: "average_teleop_hub_points", headerName: "Avg Teleop Hub Pts", width: 160, sortable: true },
{ field: "average_climb_points", headerName: "Avg Climb Pts", width: 120, sortable: true },
{ field: "yellow_cards", headerName: "Yellow Cards", width: 120, sortable: true },
{ field: "red_cards", headerName: "Red Cards", width: 100, sortable: true },
{ field: "fouls", headerName: "Fouls", width: 70, sortable: true },
{ field: "fouls_tech", headerName: "Tech Fouls", width: 100, sortable: true },
{ field: "num_disables", headerName: "Disables", width: 100, sortable: true },
{ field: "num_flips", headerName: "Flips", width: 80, sortable: true },
{ field: "matches_played", headerName: "Matches", width: 100, sortable: true },
{ field: "", headerName: "", width: 150, sortable: true },
]);
const [selectedTeams, setSelectedTeams] = useState([]);
const gridRef = useRef();
const onSelectionChanged = useCallback(() => {
let selectedRows = gridRef.current.api.getSelectedRows();
// var selectedRowsString = "";
// var maxToShow = 5;
let selectedTeams = [];
selectedRows.forEach(function (selectedRow, index) {
selectedTeams.push(selectedRow.id);
});
// if (selectedRows.length > maxToShow) {
// var othersCount = selectedRows.length - maxToShow;
// selectedRowsString += " and " + othersCount + " other" + (othersCount !== 1 ? "s" : "");
// }
// document.querySelector("#selectedRows").innerHTML = selectedRowsString;
setSelectedTeams(selectedTeams);
}, [selectedTeams]);
// const { processedDataBucket, setProcessedDataBucket } = pdbCtx;
// console.log(pdbCtx);
//format data for the data grid
let grid_data = [];
//turns the values of the key value pairs in the list into an array
console.log(processedDataBucket.teamData);
let team_data_array = Object.values(processedDataBucket.teamData);
// let team_data_array = Array.from(processedDataBucket.teamData);
const roundPlaces = (n, d) => Math.round(n * Math.pow(10, d)) / Math.pow(10, d);
// team_data_array.forEach((value, index, array) => {
for (const property in processedDataBucket.teamData) {
console.log(property);
let value = processedDataBucket.teamData[property];
console.log(value);
grid_data.push({
id: value.team_number,
average_auto_points: roundPlaces(value.average_auto_points, 2),
average_teleop_hub_points: roundPlaces(value.average_teleop_hub_points, 2),
average_climb_points: roundPlaces(value.average_climb_points, 2),
average_total_match_points: roundPlaces(value.average_total_match_points, 2),
});
}
// });
console.log(grid_data);
return (
<div>
{/* <Pie data={makePieChartData(pdb, 4388)} /> */}
<Box
sx={{
height: "600px",
height: "400px",
m: 2,
}}
>
<DataGrid
rows={grid_data}
columns={[
{ field: "id", headerName: "Team", width: 100 },
{ field: "average_total_match_points", headerName: "Avg Total Pts", width: 150 },
{ field: "average_auto_points", headerName: "Avg Auto Pts", width: 150 },
{ field: "average_teleop_hub_points", headerName: "Avg Teleop Hub Pts", width: 190 },
{ field: "average_climb_points", headerName: "Avg Climb Pts", width: 150 },
// { field: "matched_played", headerName: "Matches", width: 100 },
]}
checkboxSelection
pageSize={15}
rowsPerPageOptions={[15]}
/>
{/* <h2>{JSON.stringify(selectedTeams)}</h2> */}
<div className="ag-theme-alpine-dark" style={{ height: 400, width: "100%" }}>
<AgGridReact ref={gridRef} rowData={rowData} columnDefs={columnDefs} rowSelection={"multiple"} rowMultiSelectWithClick={true} onSelectionChanged={onSelectionChanged}></AgGridReact>
</div>
</Box>
{/* <Chart
options={{
chart: {
width: 384,
type: "pie",
},
labels: ["None", "Low", "Mid", "High", "Transversal"],
}}
series={pdb.teamData[4388].climb_counts}
type="pie"
width={380}
/> */}
<AnalyticsPanel selectedTeams={selectedTeams} />
</div>
);
//}}
+39 -24
View File
@@ -1,12 +1,17 @@
import React from "react";
import { useLocalDb } from "../DbContext";
import React, { useCallback } from "react";
import { useLocalDb, useRemoteDb } from "../DbContext";
import "./InputPage.css";
import { Formik, FastField, Form } from "formik";
import InputNumberField from "../components/InputNumberField.jsx";
import { TextField, Button, Grid, FormRow, Divider, Checkbox, Radio, FormControlLabel, FormControl, FormLabel, RadioGroup, IconButton, InputAdornment, Box } from "@mui/material";
import { useProcessedDataBucket } from "../ProcessedDataBucketContext";
import { getProcessedDataBucket, updateProcessedDataBucket } from "../ProcessedDataBucket";
const InputPage = () => {
const localdb = useLocalDb();
let { localdb, setLocaldb } = useLocalDb();
let { remotedb, setRemotedb } = useRemoteDb();
const { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
let panel_sx = {
display: "flex",
flexDirection: { xs: "column", sm: "row" },
@@ -20,6 +25,36 @@ const InputPage = () => {
borderRadius: "10px",
boxShadow: 7,
};
const onSubmit = useCallback(
(values, { setSubmitting, resetForm }) => {
// setTimeout(() => {
localdb
.put({
// _id: new Date().toISOString(),
_id: "match_" + values.match_number + "_team_" + values.team_number,
...values,
})
.then((result) => {
alert("Input Saved Successfully!");
console.log(result);
console.log(localdb);
localdb.replicate.to(remotedb, {
live: true,
});
})
.catch((err) => {
console.log("Failed To Save Input!");
alert(err);
});
// alert(JSON.stringify(values, null, 2));
// resetForm(); //Hah tobad
setSubmitting(false);
// }, 400);
updateProcessedDataBucket(localdb, setProcessedDataBucket);
},
[localdb, remotedb, setProcessedDataBucket, updateProcessedDataBucket]
);
return (
<div>
<br />
@@ -47,27 +82,7 @@ const InputPage = () => {
disabled: false,
}}
validateOnChange="false"
onSubmit={(values, { setSubmitting, resetForm }) => {
setTimeout(() => {
localdb
.put({
_id: new Date().toISOString(),
...values,
})
.then((result) => {
alert("Input Saved Successfully!");
console.log(result);
console.log(localdb);
})
.catch((err) => {
console.log("Failed To Save Input!");
alert(err);
});
// alert(JSON.stringify(values, null, 2));
// resetForm(); //Hah tobad
setSubmitting(false);
}, 400);
}}
onSubmit={onSubmit}
>
{({ values, setValues, errors, touched, handleChange, handleBlur, handleSubmit, isSubmitting }) => (
<Form>
+9 -3
View File
@@ -1,12 +1,18 @@
import React from "react";
import "./WelcomePage.css";
import "../App.css";
import DbChooser from "../components/DbChooser";
import { Box } from "@mui/material";
const WelcomePage = () => {
return (
<div className="welcome">
<h1>Welcome to Ridgebotics Scouting Web Application 2022</h1>
{/* <img src="/WelcomePageImage.webp" /> */}
<img src="/picgoeshard.jpg" />
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
<h1>Welcome to Ridgebotics Scouting Web Application 2022</h1>
{/* <img src="/WelcomePageImage.webp" /> */}
{/* <img src="/picgoeshard.jpg" /> */}
<DbChooser />
</Box>
</div>
);
};