From f4b201905e25529be586ff3f5c4f2aabac931523 Mon Sep 17 00:00:00 2001 From: Bill Radjewski Date: Thu, 30 Jan 2025 18:23:20 -0500 Subject: [PATCH] feat: adjusted efficiency ratings --- src/app/ratings/controller.ts | 20 +++++++++- src/app/ratings/service.ts | 70 ++++++++++++++++++++++++++++++++++- src/app/ratings/types.ts | 16 ++++++++ src/config/types/db.d.ts | 10 +++++ 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/app/ratings/controller.ts b/src/app/ratings/controller.ts index 7bf9203..d6f4871 100644 --- a/src/app/ratings/controller.ts +++ b/src/app/ratings/controller.ts @@ -1,8 +1,8 @@ import { Route, Tags, Controller, Get, Query, Middlewares } from 'tsoa'; import middlewares from '../../config/middleware'; -import { SrsInfo } from './types'; -import { getSrs } from './service'; +import { AdjustedEfficiencyInfo, SrsInfo } from './types'; +import { getAdjustedEfficiency, getSrs } from './service'; @Route('ratings') @Middlewares(middlewares.standard) @@ -23,4 +23,20 @@ export class RatingsController extends Controller { ): Promise { return await getSrs(season, team, conference); } + + /** + * Retrieves adjusted efficiency ratings for the provided season, team, or conference. + * @param season Optional season filter + * @param team Optional team filter + * @param conference Optional conference abbreviation filter + * @isInt season + */ + @Get('adjusted') + public async getAdjustedEfficiency( + @Query() season?: number, + @Query() team?: string, + @Query() conference?: string, + ): Promise { + return await getAdjustedEfficiency(season, team, conference); + } } diff --git a/src/app/ratings/service.ts b/src/app/ratings/service.ts index f1a8d65..2d91941 100644 --- a/src/app/ratings/service.ts +++ b/src/app/ratings/service.ts @@ -1,6 +1,6 @@ import { db } from '../../config/database'; -import { SrsInfo } from './types'; +import { AdjustedEfficiencyInfo, SrsInfo } from './types'; export const getSrs = async ( year?: number, @@ -61,3 +61,71 @@ export const getSrs = async ( rating: Number(r.rating), })); }; + +export const getAdjustedEfficiency = async ( + year?: number, + team?: string, + conference?: string, +): Promise => { + let query = db + .selectFrom('adjustedEfficiency') + .innerJoin('team', 'team.id', 'adjustedEfficiency.teamId') + .innerJoin('conferenceTeam', (join) => + join + .onRef('conferenceTeam.teamId', '=', 'team.id') + .onRef('conferenceTeam.startYear', '<=', 'adjustedEfficiency.season') + .on((eb) => + eb.or([ + eb( + 'conferenceTeam.endYear', + '>=', + eb.ref('adjustedEfficiency.season'), + ), + eb('conferenceTeam.endYear', 'is', null), + ]), + ), + ) + .innerJoin('conference', 'conference.id', 'conferenceTeam.conferenceId') + .select([ + 'adjustedEfficiency.season', + 'adjustedEfficiency.teamId', + 'team.school as team', + 'conference.abbreviation as conference', + 'adjustedEfficiency.offense as offensiveRating', + 'adjustedEfficiency.defense as defensiveRating', + 'adjustedEfficiency.net as netRating', + ]) + .orderBy('adjustedEfficiency.season', 'desc') + .orderBy('adjustedEfficiency.net', 'desc'); + + if (year) { + query = query.where('adjustedEfficiency.season', '=', year); + } + + if (team) { + query = query.where( + (eb) => eb.fn('lower', [eb.ref('team.school')]), + '=', + team.toLowerCase(), + ); + } + + if (conference) { + query = query.where( + (eb) => eb.fn('lower', [eb.ref('conference.abbreviation')]), + '=', + conference.toLowerCase(), + ); + } + + const ratings = await query.execute(); + return ratings.map((r) => ({ + season: r.season, + teamId: r.teamId, + team: r.team, + conference: r.conference, + offensiveRating: Number(r.offensiveRating), + defensiveRating: Number(r.defensiveRating), + netRating: Number(r.netRating), + })); +}; diff --git a/src/app/ratings/types.ts b/src/app/ratings/types.ts index ba42fc5..4fd708a 100644 --- a/src/app/ratings/types.ts +++ b/src/app/ratings/types.ts @@ -11,3 +11,19 @@ export interface SrsInfo { conference: string; rating: number; } + +export interface AdjustedEfficiencyInfo { + /** + * @isInt + */ + season: number; + /** + * @isInt + */ + teamId: number; + team: string; + conference: string; + offensiveRating: number; + defensiveRating: number; + netRating: number; +} diff --git a/src/config/types/db.d.ts b/src/config/types/db.d.ts index fcf9dd6..6e32d83 100644 --- a/src/config/types/db.d.ts +++ b/src/config/types/db.d.ts @@ -46,6 +46,15 @@ export type SeasonType = 'postseason' | 'preseason' | 'regular'; export type Timestamp = ColumnType; +export interface AdjustedEfficiency { + defense: Numeric; + id: Generated; + net: Numeric; + offense: Numeric; + season: number; + teamId: number; +} + export interface Athlete { dob: Timestamp | null; firstName: string | null; @@ -317,6 +326,7 @@ export interface Venue { } export interface DB { + adjustedEfficiency: AdjustedEfficiency; athlete: Athlete; athleteTeam: AthleteTeam; conference: Conference;