From 461c9ef2137397202fe2f5d860f9c5638e49a739 Mon Sep 17 00:00:00 2001 From: Asaf Meizner <75739196+AsafMeizner@users.noreply.github.com> Date: Sun, 13 Oct 2024 05:13:38 +0300 Subject: [PATCH] fixed electron issue, added team selection in all teams, fixed some bugs and added some graphs --- android/app/release/output-metadata.json | 20 +++ electron/package.json | 2 +- electron/src/index.ts | 3 +- electron/src/setup.ts | 10 +- .../chart-configs/specificTeam/configs.js | 131 +++++++++++++++++- .../chart-configs/specificTeam/functions.js | 68 +++++++++ src/components/charts/MultiNumberDisplay.js | 5 +- src/components/charts/barChart.js | 7 +- src/pages/Visualization.css | 119 ++++++++++++++++ src/pages/Visualization.js | 90 +++++++++--- src/quizData.json | 4 +- 11 files changed, 429 insertions(+), 30 deletions(-) create mode 100644 android/app/release/output-metadata.json diff --git a/android/app/release/output-metadata.json b/android/app/release/output-metadata.json new file mode 100644 index 0000000..d711d94 --- /dev/null +++ b/android/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.mashkif.app", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/electron/package.json b/electron/package.json index ce9c8d4..1a7bb4a 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "MAshkif", - "version": "0.1.1", + "version": "0.2.5", "description": "General purpose scouting app for all applications with easy to use interface and data management", "author": { "name": "Asaf Meizner", diff --git a/electron/src/index.ts b/electron/src/index.ts index 3bd1952..dbd9563 100644 --- a/electron/src/index.ts +++ b/electron/src/index.ts @@ -26,7 +26,8 @@ const capacitorFileConfig: CapacitorElectronConfig = getCapacitorElectronConfig( // Initialize our app. You can pass menu templates into the app here. // const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig); -const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig, trayMenuTemplate, appMenuBarMenuTemplate); +// const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig, trayMenuTemplate, appMenuBarMenuTemplate); +const myCapacitorApp = new ElectronCapacitorApp(capacitorFileConfig); // If deeplinking is enabled then we will set it up here. if (capacitorFileConfig.electron?.deepLinkingEnabled) { diff --git a/electron/src/setup.ts b/electron/src/setup.ts index e7c957b..5775e04 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -52,8 +52,8 @@ export class ElectronCapacitorApp { new MenuItem({ label: 'Quit App', role: 'quit' }), ]; private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [ - { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, - { role: 'viewMenu' }, + // { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + // { role: 'viewMenu' }, ]; private mainWindowState; private loadWebApp; @@ -224,10 +224,10 @@ export function setupContentSecurityPolicy(customScheme: string): void { ...details.responseHeaders, 'Content-Security-Policy': [ electronIsDev - ? `default-src ${customScheme}://* 'unsafe-inline' devtools://* 'unsafe-eval' data:` - : `default-src ${customScheme}://* 'unsafe-inline' data:`, + ? `default-src ${customScheme}://* 'unsafe-inline' devtools://* 'unsafe-eval' data:; connect-src *` + : `default-src ${customScheme}://* 'unsafe-inline' data:; connect-src *`, ], }, }); }); -} +} \ No newline at end of file diff --git a/src/components/chart-configs/specificTeam/configs.js b/src/components/chart-configs/specificTeam/configs.js index a814f10..7bb252d 100644 --- a/src/components/chart-configs/specificTeam/configs.js +++ b/src/components/chart-configs/specificTeam/configs.js @@ -277,6 +277,20 @@ export const autoPathPerRoundConfig = (scoutingData, teamNumber) => ({ stacked: true, name: 'Midline 1 (Amp Edge)', }, + { + key: 'Path9', + label: 'Path 9', + color: '#82b5d8', + stacked: true, + name: 'Other', + }, + { + key: 'Path10', + label: 'Path 10', + color: '#c4162a', + stacked: true, + name: "Didn't Move", + } ], chartSettings: { showGridlines: true, @@ -286,6 +300,7 @@ export const autoPathPerRoundConfig = (scoutingData, teamNumber) => ({ yAxisLabel: 'Autonomous Path Taken', yAxisMin: 0, yAxisMax: 1, + xAxisLabel: 'Round Number', xAxisLabelRotation: 0, showTooltip: true, tooltipSettings: { @@ -303,6 +318,57 @@ export const autoPathPerRoundConfig = (scoutingData, teamNumber) => ({ sortOrder: 'ascending', }); +export const actualVsExpectedConfig = (scoutingData, teamNumber) => ({ + data: specificTeamFunctions.actualVsExpectedData(scoutingData, teamNumber), + title: `Actual vs. Expected Autonomous Scores for Team ${teamNumber}`, + scoringTypes: [ + { + key: 'actualScore', + label: 'Actual Score', + color: '#73bf69', + name: 'Actual Score', + }, + { + key: 'expectedScore', + label: 'Expected Score', + color: '#ff8042', + name: 'Expected Score', + }, + { + key: 'deviation', + label: 'Deviation', + color: '#ff4444', + name: 'Score Deviation', + }, + ], + chartSettings: { + showGridlines: true, + gridlineColor: '#444444', + }, + xKey: 'roundNumber', + yAxisLabel: 'Score', + yAxisMin: 'auto', + yAxisMax: 'auto', + xAxisLabel: 'Round Number', + xAxisLabelRotation: 0, + showTooltip: true, + tooltipSettings: { + backgroundColor: '#333333', + borderRadius: '8px', + fontSize: '0.875rem', + cursorColor: 'rgba(255, 255, 255, 0.1)', + }, + showLegend: true, + interactiveLegend: true, + legendPosition: 'top', + responsive: true, + maintainAspectRatio: true, + showDataLabels: true, + dataLabelPosition: 'inside', + dataLabelRotation: 0, + sortOrder: 'ascending', +}); + export const autoPathUsagePieConfig = (scoutingData, teamNumber) => ({ data: specificTeamFunctions.autoPathPieData(scoutingData, teamNumber), title: `Autonomous Path Usage Percentages for Team ${teamNumber}`, @@ -355,6 +421,18 @@ export const autoPathUsagePieConfig = (scoutingData, teamNumber) => ({ color: '#d0ed57', name: 'Midline 1 (Amp Edge)', }, + { + key: 'Path9', + label: 'Path 9', + color: '#82b5d8', + name: 'Other', + }, + { + key: 'Path10', + label: 'Path 10', + color: '#c4162a', + name: "Didn't Move", + } ], chartSettings: { showTooltip: true, @@ -371,7 +449,8 @@ export const autoPathUsagePieConfig = (scoutingData, teamNumber) => ({ maintainAspectRatio: true, colors: [ '#8884d8', '#82ca9d', '#ff4444', '#ffc658', - '#ff8042', '#73bf69', '#a4de6c', '#d0ed57' + '#ff8042', '#73bf69', '#a4de6c', '#d0ed57', + '#82b5d8', '#c4162a' ], showLabels: true, labelPosition: 'outside', @@ -792,6 +871,7 @@ export const endgameClimbDataPerRoundConfig = (scoutingData, teamNumber) => ({ yAxisMin: 0, yAxisMax: 1, xAxisLabelRotation: 0, + xAxisLabel: 'Round Number', showTooltip: true, tooltipSettings: { backgroundColor: '#333333', @@ -901,6 +981,55 @@ export const trapPerRoundConfig = (scoutingData, teamNumber) => ({ dataLabelRotation: 0, }); +export const endgameTrapUsagePieConfig = (scoutingData, teamNumber) => ({ + data: specificTeamFunctions.endgameTrapPieData(scoutingData, teamNumber), + title: `Endgame Trap Score for Team ${teamNumber}`, + scoringTypes: [ + { + key: '0', + label: 'Note 0', + color: '#8884d8', + name: '0 Note', + }, + { + key: '1', + label: 'Note 1', + color: '#82ca9d', + name: '1 Note', + }, + { + key: '2', + label: 'Note 2', + color: '#ff4444', + name: '2 Note', + }, + { + key: '3', + label: 'Note 3', + color: '#ffc658', + name: '3 Note', + }, + ], + chartSettings: { + showTooltip: true, + tooltipSettings: { + backgroundColor: '#333333', + borderRadius: '8px', + fontSize: '0.875rem', + textColor: '#ffffff', + cursorColor: 'rgba(255, 255, 255, 0.1)', + }, + showLegend: true, + legendPosition: 'top', + legendLayout: 'horizontal', + maintainAspectRatio: true, + colors: ['#8884d8', '#82ca9d', '#ff4444', '#ffc658'], + showLabels: true, + labelPosition: 'outside', + }, + responsive: true, +}); + export const teamPerformanceRadarConfig = (scoutingData, teamNumber) => { // Calculate the max values across all teams for each category const maxSpeakerTeleOp = allTeamsFunctions.speakerTeleOpMax(scoutingData); // Max for Teleop Speaker Score diff --git a/src/components/chart-configs/specificTeam/functions.js b/src/components/chart-configs/specificTeam/functions.js index a93062b..8497e23 100644 --- a/src/components/chart-configs/specificTeam/functions.js +++ b/src/components/chart-configs/specificTeam/functions.js @@ -149,6 +149,8 @@ export function autoPathStackedData(scoutingData, teamNumber) { Path6: entry.a_gp_Path === "6" ? 1 : 0, Path7: entry.a_gp_Path === "7" ? 1 : 0, Path8: entry.a_gp_Path === "8" ? 1 : 0, + Path9: entry.a_gp_Path === "9" ? 1 : 0, + Path10: entry.a_gp_Path === "10" ? 1 : 0, }; }); @@ -173,6 +175,8 @@ export function autoPathPieData(scoutingData, teamNumber) { Path6: 0, Path7: 0, Path8: 0, + Path9: 0, + Path10: 0, }; teamData.forEach((entry) => { @@ -184,6 +188,8 @@ export function autoPathPieData(scoutingData, teamNumber) { pathCounts.Path6 += entry.a_gp_Path === "6" ? 1 : 0; pathCounts.Path7 += entry.a_gp_Path === "7" ? 1 : 0; pathCounts.Path8 += entry.a_gp_Path === "8" ? 1 : 0; + pathCounts.Path9 += entry.a_gp_Path === "9" ? 1 : 0; + pathCounts.Path10 += entry.a_gp_Path === "10" ? 1 : 0; }); return Object.keys(pathCounts).map((pathKey) => ({ @@ -533,6 +539,33 @@ export function endgameTrapPerRound(scoutingData, teamNumber) { return trapData; } +export function endgameTrapPieData(scoutingData, teamNumber) { + const teamData = scoutingData.filter((entry) => entry.teamNumber === teamNumber); + + if (!teamData.length) { + return []; + } + + const trapCounts = { + '0': 0, + '1': 0, + '2': 0, + '3': 0, + }; + + teamData.forEach((entry) => { + const trapScore = entry.cn || 0; // assuming cn represents the trap score + if (trapScore >= 0 && trapScore <= 3) { + trapCounts[trapScore] += 1; + } + }); + + return Object.keys(trapCounts).map((trapKey) => ({ + name: `${trapKey} Note`, + value: trapCounts[trapKey], + })); +} + export function speakerAutoAverage(scoutingData, teamNumber) { const teamData = scoutingData.filter((entry) => entry.teamNumber === teamNumber); @@ -638,4 +671,39 @@ export function maxEndPositionForMatch(scoutingData, teamNumber) { }, 0); return maxScore; +} + + +export function actualVsExpectedData(scoutingData, teamNumber) { + const expectedScoresByPath = { + Path1: 3, // Speaker 3 (Source Side) + Path2: 2, // Speaker 2 (Middle) + Path3: 1, // Speaker 1 (Amp Side) + Path4: 5, // Midline 5 (Source Edge) + Path5: 4, // Midline 4 + Path6: 3, // Midline 3 (Middle) + Path7: 2, // Midline 2 + Path8: 1, // Midline 1 (Amp Edge) + Path9: 1, // Other + Path10: 0, // Didn't Move + }; + const teamData = scoutingData.filter((entry) => entry.teamNumber === teamNumber); + + if (!teamData.length) { return []; } + + return teamData.map((entry) => { + const roundNumber = entry.matchNumber; + const actualScore = entry.ausc || 0; // Autonomous Speaker Score + + // Find the path taken and expected score + let pathTaken = Object.keys(expectedScoresByPath).find(path => entry.a_gp_Path === path.slice(4)); + let expectedScore = expectedScoresByPath[pathTaken] || 0; + + return { + roundNumber, + actualScore, + expectedScore, + deviation: actualScore - expectedScore // Calculate the deviation + }; + }); } \ No newline at end of file diff --git a/src/components/charts/MultiNumberDisplay.js b/src/components/charts/MultiNumberDisplay.js index c8176df..9632c15 100644 --- a/src/components/charts/MultiNumberDisplay.js +++ b/src/components/charts/MultiNumberDisplay.js @@ -3,7 +3,7 @@ import { ResponsiveContainer } from 'recharts'; const MultiNumberDisplay = ({ config }) => { const defaultConfig = { - mainTitle: '', // New main title in config + mainTitle: '', values: [ { color: '#00acc1', @@ -60,6 +60,9 @@ const MultiNumberDisplay = ({ config }) => { +
+ {`${percentage.toFixed(2)}%`} +
); })} diff --git a/src/components/charts/barChart.js b/src/components/charts/barChart.js index 503bb84..78f409c 100644 --- a/src/components/charts/barChart.js +++ b/src/components/charts/barChart.js @@ -18,12 +18,15 @@ const CustomBarShape = (props) => { gradient, } = props; + const adjustedY = height < 0 ? y + height : y; + const adjustedHeight = Math.abs(height); + return (