From 9eb38a8c979aabb16014eb8ec94eb7c5d29d838d Mon Sep 17 00:00:00 2001 From: karnishein <214869392+karnishein@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:39:39 +0300 Subject: [PATCH 1/3] karni- started working on the better router --- apps/scouting/backend/src/movement/stats.ts | 13 +++++-- .../backend/src/routes/general-router.ts | 11 +++++- .../backend/src/routes/tinder-router.ts | 36 +++++++++---------- packages/scouting_types/rebuilt/Movement.ts | 3 +- .../scouting_types/rebuilt/fuel/FuelTypes.ts | 21 +---------- .../scouting_types/rebuilt/general/index.ts | 32 +++++++++++++++++ packages/scouting_types/rebuilt/index.ts | 1 + 7 files changed, 74 insertions(+), 43 deletions(-) create mode 100644 packages/scouting_types/rebuilt/general/index.ts diff --git a/apps/scouting/backend/src/movement/stats.ts b/apps/scouting/backend/src/movement/stats.ts index 6ea1be21..36e79a8c 100644 --- a/apps/scouting/backend/src/movement/stats.ts +++ b/apps/scouting/backend/src/movement/stats.ts @@ -2,8 +2,15 @@ import { calculateSum } from "@repo/array-functions"; import type { ScoutingForm } from "@repo/scouting_types"; +import { MovementEvent } from "@repo/scouting_types/rebuilt/Movement"; - -export const findTimesStuckOnBump = (forms: ScoutingForm[]) => { - return calculateSum(forms, (form) => Number(!form.tele.movement.bumpStuck)); +export const findTimesMovementEvent = ( + forms: ScoutingForm[], + event: MovementEvent, +) => { + return calculateSum(forms, (form) => + Number(form.auto.movement[event]) + form.tele.movement[event] + ? Number(form.tele.movement[event]) + : 0, + ); }; diff --git a/apps/scouting/backend/src/routes/general-router.ts b/apps/scouting/backend/src/routes/general-router.ts index 81412878..b9c0513b 100644 --- a/apps/scouting/backend/src/routes/general-router.ts +++ b/apps/scouting/backend/src/routes/general-router.ts @@ -19,6 +19,7 @@ import { calculateAverageClimbsScore } from "../climb/score"; import { formsToFuelData } from "../fuel/fuel-general"; import { getAllBPSes } from "./bps-router"; import { isEmpty } from "@repo/array-functions"; +import { findTimesMovementEvent } from "../movement/stats"; export const generalRouter = Router(); @@ -38,14 +39,22 @@ const formsToGeneralData = ( const generalData: GeneralData = { teamNumber: Number(teamNumber), fuelData: fuelData, - highestClimbLevel: findMaxClimbLevel(teamForms), avarageClimbPoints: { fullGame: calculateAverageClimbsScore(teamForms).auto + calculateAverageClimbsScore(teamForms).tele, auto: calculateAverageClimbsScore(teamForms).auto, tele: calculateAverageClimbsScore(teamForms).tele, + highestClimbLevel: findMaxClimbLevel(teamForms), }, + opr: , + movement: { + passTrenchCount: findTimesMovementEvent(teamForms, "trenchPass"), + passBumpCount: findTimesMovementEvent(teamForms, "bumpPass"), + stuckBumpCount: findTimesMovementEvent(teamForms, "bumpStuck"), + }, + epa: 0, + averagePointsPerMatch: 0, }; return generalData; diff --git a/apps/scouting/backend/src/routes/tinder-router.ts b/apps/scouting/backend/src/routes/tinder-router.ts index 65751263..973a0fb8 100644 --- a/apps/scouting/backend/src/routes/tinder-router.ts +++ b/apps/scouting/backend/src/routes/tinder-router.ts @@ -3,13 +3,7 @@ import { Router } from "express"; import { pipe } from "fp-ts/lib/function"; import { getFormsCollection } from "./forms-router"; -import { - flatMap, - filterOrElse, - map, - fold, - bindTo, -} from "fp-ts/lib/TaskEither"; +import { flatMap, filterOrElse, map, fold, bindTo } from "fp-ts/lib/TaskEither"; import { flatTryCatch, foldResponse, mongofyQuery } from "@repo/flow-utils"; import { StatusCodes } from "http-status-codes"; import { @@ -22,7 +16,7 @@ import { formsToFuelData, } from "../fuel/fuel-general"; import { findMaxClimbLevel } from "../climb/calculations"; -import { findTimesStuckOnBump } from "../movement/stats"; +import { findTimesMovementEvent } from "../movement/stats"; import { isSingleTeam } from "../verification/functions"; import { getTeamBPSes } from "./bps-router"; import { firstElement, isEmpty } from "@repo/array-functions"; @@ -37,7 +31,7 @@ const createTinder = (forms: ScoutingForm[], bpses: Record) => ({ maxClimbLevel: findMaxClimbLevel(forms), }, movement: { - stuckOnBump: findTimesStuckOnBump(forms), + stuckOnBump: findTimesMovementEvent(forms, "bumpStuck"), }, }); @@ -51,10 +45,13 @@ tinderRouter.get("/", (req, res) => reason: `DB Error: ${error}`, }), ), - filterOrElse((forms) => !isEmpty(forms), () => ({ - status: StatusCodes.BAD_REQUEST, - reason: "Tinder Team Error: No forms match the query.", - })), + filterOrElse( + (forms) => !isEmpty(forms), + () => ({ + status: StatusCodes.BAD_REQUEST, + reason: "Tinder Team Error: No forms match the query.", + }), + ), filterOrElse(isSingleTeam, () => ({ status: StatusCodes.BAD_REQUEST, reason: @@ -63,11 +60,14 @@ tinderRouter.get("/", (req, res) => map(excludeNoShowForms), - filterOrElse((forms) => !isEmpty(forms), () => ({ - status: StatusCodes.BAD_REQUEST, - reason: - "Tinder Team Error: No valid scouting data (all matches marked no-show).", - })), + filterOrElse( + (forms) => !isEmpty(forms), + () => ({ + status: StatusCodes.BAD_REQUEST, + reason: + "Tinder Team Error: No valid scouting data (all matches marked no-show).", + }), + ), flatMap((forms) => getTeamBPSes({ [firstElement(forms).teamNumber]: forms }), diff --git a/packages/scouting_types/rebuilt/Movement.ts b/packages/scouting_types/rebuilt/Movement.ts index 56f24b6c..abe11ccd 100644 --- a/packages/scouting_types/rebuilt/Movement.ts +++ b/packages/scouting_types/rebuilt/Movement.ts @@ -19,6 +19,7 @@ export const defaultMovement: t.TypeOf = { bumpStuck: false, }; +export type MovementEvent = keyof typeof defaultMovement; export type AutoMovement = t.TypeOf; -export type TeleMovement = t.TypeOf +export type TeleMovement = t.TypeOf; export type Movement = AutoMovement | TeleMovement; diff --git a/packages/scouting_types/rebuilt/fuel/FuelTypes.ts b/packages/scouting_types/rebuilt/fuel/FuelTypes.ts index 295b4bf3..bda70095 100644 --- a/packages/scouting_types/rebuilt/fuel/FuelTypes.ts +++ b/packages/scouting_types/rebuilt/fuel/FuelTypes.ts @@ -1,29 +1,10 @@ //בס"ד +import { GeneralFuelData } from "../general"; import type { ClimbLevel, Point } from "../scouting_form"; -export interface GeneralFuelData { - fullGame: FuelObject; - auto: FuelObject; - tele: FuelObject; -} - -export interface GeneralClimbData { - fullGame: number; - auto: number; - tele: number; -} - -export interface GeneralData { - teamNumber: number; - fuelData: GeneralFuelData; - highestClimbLevel: ClimbLevel; - avarageClimbPoints: GeneralClimbData; -} - export type GameTime = keyof GeneralFuelData; - export type FuelEvents = "scored" | "shot" | "missed" | "passed"; export type FuelObject = GameObject< diff --git a/packages/scouting_types/rebuilt/general/index.ts b/packages/scouting_types/rebuilt/general/index.ts new file mode 100644 index 00000000..5fe7a01a --- /dev/null +++ b/packages/scouting_types/rebuilt/general/index.ts @@ -0,0 +1,32 @@ +import { FuelObject } from "../fuel"; +import { ClimbLevel } from "../scouting_form"; + +export interface GeneralFuelData { + fullGame: FuelObject; + auto: FuelObject; + tele: FuelObject; +} + +export interface GeneralClimbData { + fullGame: number; + auto: number; + tele: number; + highestClimbLevel: ClimbLevel; +} + +interface GeneralMovementData { + passTrenchCount: number; + passBumpCount: number; + stuckBumpCount: number; +} + +export interface GeneralData { + teamNumber: number; + fuelData: GeneralFuelData; + avarageClimbPoints: GeneralClimbData; + movement: GeneralMovementData; + opr: number; + epa: number; + averagePointsPerMatch: number; +} + diff --git a/packages/scouting_types/rebuilt/index.ts b/packages/scouting_types/rebuilt/index.ts index 89ab6233..ecbc2c16 100644 --- a/packages/scouting_types/rebuilt/index.ts +++ b/packages/scouting_types/rebuilt/index.ts @@ -8,3 +8,4 @@ export type * from "./tinder"; export * from "./bps"; export * from "./super_scout"; export * from "./picklist"; +export * from "./general"; From 44642851d95aaee13c6dbe46ec86dcdcb0306c35 Mon Sep 17 00:00:00 2001 From: karnishein <214869392+karnishein@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:56:38 +0300 Subject: [PATCH 2/3] karni- finished backend --- apps/scouting/backend/src/climb/score.ts | 4 ++-- .../scouting/backend/src/game/calculations.ts | 21 +++++++++++++++++++ .../backend/src/routes/general-router.ts | 21 ++++++++++++++++--- .../scouting_types/rebuilt/general/index.ts | 3 +-- 4 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 apps/scouting/backend/src/game/calculations.ts diff --git a/apps/scouting/backend/src/climb/score.ts b/apps/scouting/backend/src/climb/score.ts index e506d88e..400f9436 100644 --- a/apps/scouting/backend/src/climb/score.ts +++ b/apps/scouting/backend/src/climb/score.ts @@ -5,8 +5,8 @@ import type { ClimbLevel, ScoutingForm } from "@repo/scouting_types"; export const CLIMB_SCORE_VALUES = { L0: 0, L1: 10, L2: 20, L3: 30 }; -const TELE_CLIMB_MULTIPLIER = 1; -const AUTO_CLIMB_MULTIPLIER = 1.5; +export const TELE_CLIMB_MULTIPLIER = 1; +export const AUTO_CLIMB_MULTIPLIER = 1.5; export const calculateAverageClimbScore = ( climbs: ClimbLevel[], isAuto: boolean, diff --git a/apps/scouting/backend/src/game/calculations.ts b/apps/scouting/backend/src/game/calculations.ts new file mode 100644 index 00000000..85c74d23 --- /dev/null +++ b/apps/scouting/backend/src/game/calculations.ts @@ -0,0 +1,21 @@ +//בס"ד + +import { BPS, ScoutingForm } from "@repo/scouting_types"; +import { + AUTO_CLIMB_MULTIPLIER, + calculateAverageClimbsScore, + CLIMB_SCORE_VALUES, + TELE_CLIMB_MULTIPLIER, +} from "../climb/score"; +import { calculateAverage } from "@repo/array-functions"; +import { calculateAverageScoredFuel } from "../fuel/fuel-general"; + +export const calculateTotalGamePoints = (forms: ScoutingForm[], bpses: BPS[]) => + calculateAverage( + forms, + (form) => + CLIMB_SCORE_VALUES[form.auto.climb.level] * AUTO_CLIMB_MULTIPLIER + + CLIMB_SCORE_VALUES[form.tele.climb.level] * TELE_CLIMB_MULTIPLIER + + calculateAverageScoredFuel(forms, "fullGame", bpses), + //Dror: 67676767676767676767676766767676767676767676767676767676767676767676767676767676767677666676766776767676767676767676767 + ); diff --git a/apps/scouting/backend/src/routes/general-router.ts b/apps/scouting/backend/src/routes/general-router.ts index b9c0513b..1127f50b 100644 --- a/apps/scouting/backend/src/routes/general-router.ts +++ b/apps/scouting/backend/src/routes/general-router.ts @@ -8,7 +8,9 @@ import { mongofyQuery, flatTryCatch } from "@repo/flow-utils"; import { StatusCodes } from "http-status-codes"; import { + EPA, excludeNoShowForms, + TeamOPR, type BPS, type GeneralData, type ScoutingForm, @@ -20,12 +22,17 @@ import { formsToFuelData } from "../fuel/fuel-general"; import { getAllBPSes } from "./bps-router"; import { isEmpty } from "@repo/array-functions"; import { findTimesMovementEvent } from "../movement/stats"; +import { fetchTeamsCOPRs } from "./tba-router"; +import { getTeamsEPAs } from "../middleware/epa"; +import { calculateTotalGamePoints } from "../game/calculations"; export const generalRouter = Router(); const formsToGeneralData = ( forms: ScoutingForm[], bpses: Record, + coprs?: TeamOPR, + epa?: EPA, ) => { const calculatedFuel: TeamNumberAndFuelData = formsToFuelData(bpses)(forms); @@ -35,6 +42,11 @@ const formsToGeneralData = ( const teamForms = forms.filter( (form) => form.teamNumber.toString() === teamNumber, ); + const resultOPR = {} as TeamOPR; + const resultEPA = {} as EPA; + + fetchTeamsCOPRs({ teamNumber: resultOPR }); + getTeamsEPAs({ teamNumber: resultEPA }); const generalData: GeneralData = { teamNumber: Number(teamNumber), @@ -47,14 +59,17 @@ const formsToGeneralData = ( tele: calculateAverageClimbsScore(teamForms).tele, highestClimbLevel: findMaxClimbLevel(teamForms), }, - opr: , + copr: resultOPR.fuelTotal, movement: { passTrenchCount: findTimesMovementEvent(teamForms, "trenchPass"), passBumpCount: findTimesMovementEvent(teamForms, "bumpPass"), stuckBumpCount: findTimesMovementEvent(teamForms, "bumpStuck"), }, - epa: 0, - averagePointsPerMatch: 0, + epa: resultEPA.breakdown.total_points, + averagePointsPerMatch: calculateTotalGamePoints( + forms, + bpses[teamNumber], + ), }; return generalData; diff --git a/packages/scouting_types/rebuilt/general/index.ts b/packages/scouting_types/rebuilt/general/index.ts index 5fe7a01a..404835d8 100644 --- a/packages/scouting_types/rebuilt/general/index.ts +++ b/packages/scouting_types/rebuilt/general/index.ts @@ -25,8 +25,7 @@ export interface GeneralData { fuelData: GeneralFuelData; avarageClimbPoints: GeneralClimbData; movement: GeneralMovementData; - opr: number; + copr: number | undefined; epa: number; averagePointsPerMatch: number; } - From e03e4960baec0d3c684ea0d832ca2a568507fe19 Mon Sep 17 00:00:00 2001 From: karnishein <214869392+karnishein@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:04:02 +0300 Subject: [PATCH 3/3] karni - created table (doesnt work bc of opr and epr) --- apps/scouting/backend/build.ts | 2 +- .../backend/src/routes/general-router.ts | 2 +- .../src/strategy/tabs/GeneralDataTable.tsx | 22 ++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/scouting/backend/build.ts b/apps/scouting/backend/build.ts index 0f4439ef..3890f2a2 100644 --- a/apps/scouting/backend/build.ts +++ b/apps/scouting/backend/build.ts @@ -2,7 +2,7 @@ import { build, context } from "esbuild"; import { spawn } from "child_process"; -const isDev = process.env.NODE_ENV === "DEV"; +const isDev = process.env.NODE_ENV !== "DEV"; const bundlePath = "dist/bundle.js"; diff --git a/apps/scouting/backend/src/routes/general-router.ts b/apps/scouting/backend/src/routes/general-router.ts index 1127f50b..c12d5891 100644 --- a/apps/scouting/backend/src/routes/general-router.ts +++ b/apps/scouting/backend/src/routes/general-router.ts @@ -67,7 +67,7 @@ const formsToGeneralData = ( }, epa: resultEPA.breakdown.total_points, averagePointsPerMatch: calculateTotalGamePoints( - forms, + teamForms, bpses[teamNumber], ), }; diff --git a/apps/scouting/frontend/src/strategy/tabs/GeneralDataTable.tsx b/apps/scouting/frontend/src/strategy/tabs/GeneralDataTable.tsx index 83c80621..b2fd618c 100644 --- a/apps/scouting/frontend/src/strategy/tabs/GeneralDataTable.tsx +++ b/apps/scouting/frontend/src/strategy/tabs/GeneralDataTable.tsx @@ -25,8 +25,13 @@ interface TableRow { generalFuelData: GeneralFuelData; } -export type Column = FuelEvents | "climb" | "max climb"; - +export type Column = + | FuelEvents + | "climb" + | "max climb" + | "copr" + | "epa" + | "average points"; type DataValue = ClimbLevel | number | undefined; type DataAccessor = (row: GeneralData, gameTime: GameTime) => DataValue; @@ -36,7 +41,10 @@ const columnToKey: Record = { missed: (row, gameTime) => row.fuelData[gameTime].missed, passed: (row, gameTime) => row.fuelData[gameTime].passed, climb: (row, gameTime) => row.avarageClimbPoints[gameTime], - "max climb": (row) => row.highestClimbLevel, + "max climb": (row) => row.avarageClimbPoints.highestClimbLevel, + copr: (row) => row.copr, + epa: (row) => row.epa, + "average points": (row) => row.averagePointsPerMatch, }; const fetchGeneralData = async (filters = {}) => { @@ -113,12 +121,14 @@ export const GeneralDataTable: React.FC = ({ {info.getValue()} ), }), - + createColumn("copr", "text-blue-400 font-medium"), + createColumn("epa", "text-yellow-400 font-medium"), + createColumn("average points", "text-cyan-400 font-medium"), createColumn("scored", "text-emerald-400 font-bold"), - createColumn("missed", "text-rose-500/90 font-medium"), - createColumn("passed", "text-orange-400 font-medium"), createColumn("climb", "text-purple-400 font-bold"), createColumn("max climb", "text-slate-400 uppercase text-[10px]"), + createColumn("missed", "text-rose-500/90 font-medium"), + createColumn("passed", "text-orange-400 font-medium"), ], [gameTime, sorting], );