mirror of
https://github.com/Team4388/ScoutingApp2022.git
synced 2026-06-08 16:28:04 -06:00
we ball
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+21
-21
@@ -10,23 +10,23 @@ services:
|
||||
context: ./webserver
|
||||
dockerfile: ./Dockerfile.prod
|
||||
expose:
|
||||
- 8080
|
||||
- 80
|
||||
ports:
|
||||
- "8080:8080/tcp"
|
||||
- "80:80/tcp"
|
||||
# - "80:80/tcp"
|
||||
# - "443:443/tcp"
|
||||
volumes:
|
||||
- ./webserver/nginx:/etc/nginx/:ro
|
||||
ssl-proxy:
|
||||
image: fsouza/docker-ssl-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
environment:
|
||||
- DOMAIN=10.43.88.1
|
||||
- TARGET_PORT=8080
|
||||
- TARGET_HOST=scouting-webserver-prod
|
||||
- SSL_PORT:443
|
||||
# ssl-proxy:
|
||||
# image: fsouza/docker-ssl-proxy
|
||||
# ports:
|
||||
# - "80:80"
|
||||
# - "443:443"
|
||||
# environment:
|
||||
# - DOMAIN=10.43.88.1
|
||||
# - TARGET_PORT=8080
|
||||
# - TARGET_HOST=scouting-webserver-prod
|
||||
# - SSL_PORT:443
|
||||
couchdb:
|
||||
container_name: "scouting-couchdb"
|
||||
restart: unless-stopped
|
||||
@@ -47,12 +47,12 @@ services:
|
||||
volumes:
|
||||
- ./couchdb/db.local.ini:/opt/couchdb/etc/local.ini
|
||||
- ./couchdb/data:/opt/couchdb/data
|
||||
couch-ssl-proxy:
|
||||
image: fsouza/docker-ssl-proxy
|
||||
ports:
|
||||
- "5985:5985"
|
||||
environment:
|
||||
- DOMAIN=10.43.88.1
|
||||
- TARGET_PORT=5984
|
||||
- TARGET_HOST=scouting-couchdb
|
||||
- SSL_PORT:5985
|
||||
# couch-ssl-proxy:
|
||||
# image: fsouza/docker-ssl-proxy
|
||||
# ports:
|
||||
# - "5985:5985"
|
||||
# environment:
|
||||
# - DOMAIN=10.43.88.1
|
||||
# - TARGET_PORT=5984
|
||||
# - TARGET_HOST=scouting-couchdb
|
||||
# - SSL_PORT:5985
|
||||
|
||||
@@ -16,6 +16,6 @@ FROM nginx:1-alpine
|
||||
COPY --from=build-step /app/build /usr/share/nginx/html
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
# COPY nginx/nginx.conf /etc/nginx/conf.d
|
||||
EXPOSE 8080
|
||||
EXPOSE 80
|
||||
# EXPOSE 443
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
text/xml xml;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
application/javascript js;
|
||||
application/atom+xml atom;
|
||||
application/rss+xml rss;
|
||||
|
||||
text/mathml mml;
|
||||
text/plain txt;
|
||||
text/vnd.sun.j2me.app-descriptor jad;
|
||||
text/vnd.wap.wml wml;
|
||||
text/x-component htc;
|
||||
|
||||
image/png png;
|
||||
image/svg+xml svg svgz;
|
||||
image/tiff tif tiff;
|
||||
image/vnd.wap.wbmp wbmp;
|
||||
image/webp webp;
|
||||
image/x-icon ico;
|
||||
image/x-jng jng;
|
||||
image/x-ms-bmp bmp;
|
||||
|
||||
application/font-woff woff;
|
||||
application/java-archive jar war ear;
|
||||
application/json json;
|
||||
application/mac-binhex40 hqx;
|
||||
application/msword doc;
|
||||
application/pdf pdf;
|
||||
application/postscript ps eps ai;
|
||||
application/rtf rtf;
|
||||
application/vnd.apple.mpegurl m3u8;
|
||||
application/vnd.google-earth.kml+xml kml;
|
||||
application/vnd.google-earth.kmz kmz;
|
||||
application/vnd.ms-excel xls;
|
||||
application/vnd.ms-fontobject eot;
|
||||
application/vnd.ms-powerpoint ppt;
|
||||
application/vnd.oasis.opendocument.graphics odg;
|
||||
application/vnd.oasis.opendocument.presentation odp;
|
||||
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||
application/vnd.oasis.opendocument.text odt;
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
pptx;
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
xlsx;
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
docx;
|
||||
application/vnd.wap.wmlc wmlc;
|
||||
application/x-7z-compressed 7z;
|
||||
application/x-cocoa cco;
|
||||
application/x-java-archive-diff jardiff;
|
||||
application/x-java-jnlp-file jnlp;
|
||||
application/x-makeself run;
|
||||
application/x-perl pl pm;
|
||||
application/x-pilot prc pdb;
|
||||
application/x-rar-compressed rar;
|
||||
application/x-redhat-package-manager rpm;
|
||||
application/x-sea sea;
|
||||
application/x-shockwave-flash swf;
|
||||
application/x-stuffit sit;
|
||||
application/x-tcl tcl tk;
|
||||
application/x-x509-ca-cert der pem crt;
|
||||
application/x-xpinstall xpi;
|
||||
application/xhtml+xml xhtml;
|
||||
application/xspf+xml xspf;
|
||||
application/zip zip;
|
||||
|
||||
application/octet-stream bin exe dll;
|
||||
application/octet-stream deb;
|
||||
application/octet-stream dmg;
|
||||
application/octet-stream iso img;
|
||||
application/octet-stream msi msp msm;
|
||||
|
||||
audio/midi mid midi kar;
|
||||
audio/mpeg mp3;
|
||||
audio/ogg ogg;
|
||||
audio/x-m4a m4a;
|
||||
audio/x-realaudio ra;
|
||||
|
||||
video/3gpp 3gpp 3gp;
|
||||
video/mp2t ts;
|
||||
video/mp4 mp4;
|
||||
video/mpeg mpeg mpg;
|
||||
video/quicktime mov;
|
||||
video/webm webm;
|
||||
video/x-flv flv;
|
||||
video/x-m4v m4v;
|
||||
video/x-mng mng;
|
||||
video/x-ms-asf asx asf;
|
||||
video/x-ms-wmv wmv;
|
||||
video/x-msvideo avi;
|
||||
}
|
||||
@@ -2,9 +2,10 @@ events {
|
||||
worker_connections 4096; ## Default: 1024
|
||||
}
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
location / {
|
||||
root /usr/share/nginx/html/;
|
||||
index index.html index.htm;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react-apexcharts": "^1.3.9",
|
||||
"ag-grid-community": "^26.2.1",
|
||||
"ag-grid-react": "^26.2.0",
|
||||
"formik": "^2.2.9",
|
||||
"pouchdb": "^7.2.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#00a65a" />
|
||||
<meta name="description" content="Ridgebotics Scouting App 2022" />
|
||||
<!-- disable user zooming/scaling on mobile -->
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
|
||||
@@ -19,11 +19,11 @@ function App() {
|
||||
palette: {
|
||||
mode: "dark",
|
||||
background: {
|
||||
paper: "#101515",
|
||||
paper: "#203030",
|
||||
},
|
||||
text: {
|
||||
// primary: "#edf8f3",
|
||||
// secondary: "#acd3bf",
|
||||
primary: "#edf8f3",
|
||||
secondary: "#acd3bf",
|
||||
},
|
||||
red_alliance: "#ec2e63",
|
||||
blue_alliance: "#2d74eb",
|
||||
|
||||
+69
-23
@@ -1,6 +1,6 @@
|
||||
import PouchDB from "pouchdb";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { ProcessedDataBucketContext, useProcessedDataBucket } from "./ProcessedDataBucketContext";
|
||||
import { useProcessedDataBucket } from "./ProcessedDataBucketContext";
|
||||
import { getProcessedDataBucket, updateProcessedDataBucket } from "./ProcessedDataBucket";
|
||||
|
||||
const LocalDbContext = React.createContext();
|
||||
@@ -16,12 +16,11 @@ export function useRemoteDb() {
|
||||
|
||||
export function DbProvider({ children }) {
|
||||
// const pdbCtx = useProcessedDataBucket();
|
||||
const { processedDataBucket, setProcessedDataBucket } = useContext(ProcessedDataBucketContext);
|
||||
const { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
|
||||
// console.log(pdb);
|
||||
const [localdb, setLocaldb] = useState(new PouchDB("testdata"));
|
||||
//used in development server
|
||||
const [localdb, setLocaldb] = useState(new PouchDB("denver_fr"));
|
||||
const [remotedb, setRemotedb] = useState(
|
||||
new PouchDB("http://" + window.location.hostname + ":5984/testdata", {
|
||||
new PouchDB("http://" + window.location.hostname + ":5984/denver_fr", {
|
||||
skip_setup: true,
|
||||
auth: {
|
||||
username: "scouting",
|
||||
@@ -29,25 +28,50 @@ export function DbProvider({ children }) {
|
||||
},
|
||||
})
|
||||
);
|
||||
localdb.replicate
|
||||
.from(remotedb, {
|
||||
live: true,
|
||||
retry: true,
|
||||
})
|
||||
.on("change", (change) => {
|
||||
updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
});
|
||||
// updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
// useEffect(() => {
|
||||
// setDatabaseName("denver_fr", setLocaldb, setRemotedb, setProcessedDataBucket);
|
||||
// });
|
||||
|
||||
useEffect(() => {
|
||||
console.log("TEST");
|
||||
updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
localdb
|
||||
.sync(remotedb, {
|
||||
live: true,
|
||||
retry: true,
|
||||
})
|
||||
.on("change", function (change) {
|
||||
console.log("DB CHANGED");
|
||||
updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
})
|
||||
.on("paused", function (info) {})
|
||||
.on("active", function (info) {})
|
||||
.on("error", function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
}, [localdb, setProcessedDataBucket]);
|
||||
// useEffect(() => {
|
||||
// updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
// localdb
|
||||
// .sync(remotedb, {
|
||||
// live: true,
|
||||
// retry: true,
|
||||
// })
|
||||
// .on("change", function (change) {
|
||||
// console.log("DB CHANGED");
|
||||
// updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
// })
|
||||
// .on("paused", function (info) {
|
||||
// console.log("sync paused");
|
||||
// // console.log(info);
|
||||
// })
|
||||
// .on("active", function (info) {
|
||||
// console.log("sync active");
|
||||
// // console.log(info);
|
||||
// })
|
||||
// .on("denied", function (info) {
|
||||
// console.log("sync denied");
|
||||
// // console.log(info);
|
||||
// })
|
||||
// .on("complete", function (info) {
|
||||
// console.log("sync complete");
|
||||
// // console.log(info);
|
||||
// })
|
||||
// .on("error", function (err) {
|
||||
// console.error(err);
|
||||
// });
|
||||
// }, [localdb, setProcessedDataBucket]);
|
||||
|
||||
return (
|
||||
<LocalDbContext.Provider value={{ localdb, setLocaldb }}>
|
||||
@@ -55,3 +79,25 @@ export function DbProvider({ children }) {
|
||||
</LocalDbContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function setDatabaseName(name, setLocaldb, setRemotedb, setProcessedDataBucket) {
|
||||
let localdb = new PouchDB(name);
|
||||
let remotedb = new PouchDB("http://" + window.location.hostname + ":5984/" + name, {
|
||||
skip_setup: true,
|
||||
auth: {
|
||||
username: "scouting",
|
||||
password: "Ridgebotics",
|
||||
},
|
||||
});
|
||||
localdb.replicate
|
||||
.from(remotedb, {
|
||||
live: true,
|
||||
retry: true,
|
||||
})
|
||||
.on("change", (change) => {
|
||||
updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
});
|
||||
updateProcessedDataBucket(localdb, setProcessedDataBucket);
|
||||
setLocaldb(localdb);
|
||||
setRemotedb(remotedb);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
//}}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,16 +8,14 @@ export function updateProcessedDataBucket(db, setProcessedDataBucket) {
|
||||
let teamData = {};
|
||||
let matchData = {};
|
||||
|
||||
// console.log(result);
|
||||
result.rows.forEach((dbentry) => {
|
||||
let doc = dbentry.doc;
|
||||
// console.log(doc);
|
||||
|
||||
//if there's no processed data on a team yet, create a default data entry
|
||||
if (typeof teamData[doc.team_number] === "undefined") {
|
||||
teamData[doc.team_number] = {
|
||||
team_number: doc.team_number,
|
||||
matched_played: 0,
|
||||
matches_played: 0,
|
||||
data_sets: {
|
||||
upper_hub_auto: [],
|
||||
lower_hub_auto: [],
|
||||
@@ -44,7 +42,7 @@ export function updateProcessedDataBucket(db, setProcessedDataBucket) {
|
||||
|
||||
//add this game's data to the respective team data:
|
||||
let thisTeamData = teamData[doc.team_number];
|
||||
thisTeamData.matched_played++;
|
||||
thisTeamData.matches_played++;
|
||||
|
||||
let auto_points = (parseInt(doc.taxi_auto) ? 2 : 0) + parseInt(doc.upper_hub_auto) * 4 + parseInt(doc.lower_hub_auto) * 2;
|
||||
let teleop_hub_points = parseInt(doc.upper_hub_teleop) * 2 + parseInt(doc.lower_hub_teleop) * 1;
|
||||
@@ -74,10 +72,10 @@ export function updateProcessedDataBucket(db, setProcessedDataBucket) {
|
||||
//sum of all points in the match points data set for this team
|
||||
//function for getting the sum of an array, use in reduce function of array
|
||||
const sum = (accum, current) => accum + current;
|
||||
thisTeamData.average_auto_points = thisTeamData.data_sets.auto_points.reduce(sum, 0) / thisTeamData.matched_played;
|
||||
thisTeamData.average_teleop_hub_points = thisTeamData.data_sets.teleop_hub_points.reduce(sum, 0) / thisTeamData.matched_played;
|
||||
thisTeamData.average_climb_points = thisTeamData.data_sets.climb_points.reduce(sum, 0) / thisTeamData.matched_played;
|
||||
thisTeamData.average_total_match_points = thisTeamData.data_sets.total_match_points.reduce(sum, 0) / thisTeamData.matched_played;
|
||||
thisTeamData.average_auto_points = thisTeamData.data_sets.auto_points.reduce(sum, 0) / thisTeamData.matches_played;
|
||||
thisTeamData.average_teleop_hub_points = thisTeamData.data_sets.teleop_hub_points.reduce(sum, 0) / thisTeamData.matches_played;
|
||||
thisTeamData.average_climb_points = thisTeamData.data_sets.climb_points.reduce(sum, 0) / thisTeamData.matches_played;
|
||||
thisTeamData.average_total_match_points = thisTeamData.data_sets.total_match_points.reduce(sum, 0) / thisTeamData.matches_played;
|
||||
});
|
||||
setProcessedDataBucket({ teamData: teamData, matchData: matchData });
|
||||
})
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { Box, InputLabel, MenuItem, FormControl, Select } from "@mui/material";
|
||||
import { setDatabaseName, useLocalDb, useRemoteDb } from "../DbContext";
|
||||
import { useProcessedDataBucket } from "../ProcessedDataBucketContext";
|
||||
|
||||
const DbChooser = (props) => {
|
||||
const { localdb, setLocaldb } = useLocalDb();
|
||||
const { remotedb, setRemotedb } = useRemoteDb();
|
||||
const { processedDataBucket, setProcessedDataBucket } = useProcessedDataBucket();
|
||||
|
||||
const [dbname, setDbName] = React.useState(localdb.name);
|
||||
|
||||
const handleChange = useCallback((event) => {
|
||||
console.log(event.target.value);
|
||||
// setAge(event.target.value);
|
||||
setDbName(event.target.value);
|
||||
setDatabaseName(event.target.value, setLocaldb, setRemotedb, setProcessedDataBucket);
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<Box sx={{ width: 400 }}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Database</InputLabel>
|
||||
<Select value={dbname} label="Database Name" onChange={handleChange}>
|
||||
<MenuItem value={"denver_practice"}>Denver Practice Matches</MenuItem>
|
||||
<MenuItem value={"denver_fr"}>Denver For Real</MenuItem>
|
||||
{/* <MenuItem value={"utah_practice"}>Utah Practice Matches</MenuItem>
|
||||
<MenuItem value={"utah_fr"}>Utah For Real</MenuItem> */}
|
||||
<MenuItem value={"testdata"}>Test Data</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DbChooser;
|
||||
@@ -4,5 +4,5 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
.toolbar_logo a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 2rem;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.toolbar_spacer {
|
||||
|
||||
@@ -10,7 +10,7 @@ const Toolbar = props => {
|
||||
<header className="toolbar">
|
||||
<nav className="toolbar_navigation">
|
||||
<DrawerButton click={props.drawerClickHandler} />
|
||||
<div className = "toolbar_logo"><Link to="/">Ridgebotics Scouting <3</Link></div>
|
||||
<div className = "toolbar_logo"><Link to="/">Ridgebotics Scouting 💙</Link></div>
|
||||
<div className = "toolbar_spacer" />
|
||||
<div className = "toolbar_items">
|
||||
<PagesList />
|
||||
|
||||
Reference in New Issue
Block a user