Skip to content

Commit

Permalink
Feature: transfer portal rankings
Browse files Browse the repository at this point in the history
  • Loading branch information
esmalleydev committed Apr 15, 2024
1 parent 886eb60 commit 5ca83d8
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 39 deletions.
26 changes: 21 additions & 5 deletions app/cbb/ranking/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const getHeaderColumns = ({ rankView }: {rankView: string}) => {
'name': {
id: 'name',
numeric: false,
label: (rankView === 'player' ? 'Player' : (rankView === 'conference' ? 'Conference' :'Team')),
tooltip: (rankView === 'player' ? 'Player name' : (rankView === 'conference' ? 'Conference name' :'Team name')),
label: (rankView === 'player' || rankView === 'transfer' ? 'Player' : (rankView === 'conference' ? 'Conference' :'Team')),
tooltip: (rankView === 'player' || rankView === 'transfer' ? 'Player name' : (rankView === 'conference' ? 'Conference name' :'Team name')),
'sticky': true,
'disabled': true,
},
Expand Down Expand Up @@ -542,13 +542,29 @@ export const getHeaderColumns = ({ rankView }: {rankView: string}) => {
'sort': 'lower',
},
});
} else if (rankView === 'player') {
} else if (rankView === 'player' || rankView === 'transfer') {
if (rankView === 'transfer') {
Object.assign(headCells, {
'committed': {
id: 'committed',
numeric: false,
label: 'Committed',
tooltip: 'Player committed',
},
'committed_team_name': {
id: 'committed_team_name',
numeric: false,
label: 'New team',
tooltip: 'New team player committed',
},
});
}
Object.assign(headCells, {
'team_name': {
id: 'team_name',
numeric: false,
label: 'Team',
tooltip: 'Team name',
label: (rankView === 'transfer' ? 'Prev. team' : 'Team'),
tooltip: (rankView === 'transfer' ? 'Previous team name' : 'Team name'),
},
'games': {
id: 'games',
Expand Down
45 changes: 43 additions & 2 deletions app/cbb/ranking/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export async function generateMetadata(
} else if (view === 'conference') {
title = 'sRating | College basketball conference ranking';
description = 'View statistic ranking for each conference';
} else if (view === 'transfer') {
title = 'sRating | College basketball transfer portal ranking';
description = 'College basketball transfer portal tool, search, rank all players';
}

return {
Expand Down Expand Up @@ -55,20 +58,58 @@ async function getData(searchParams) {
const view = searchParams?.view || 'team';

let fxn = 'getTeamRanking';
if (view === 'player') {
if (view === 'player' || view === 'transfer') {
fxn = 'getPlayerRanking';
} else if (view === 'conference') {
fxn = 'getConferenceRanking';
}

const data = await useServerAPI({
let data = await useServerAPI({
'class': 'cbb_ranking',
'function': fxn,
'arguments': {
'season': season
}
}, {'revalidate': seconds});

if (view === 'transfer') {
data = Object.assign({}, data);
const cbb_transfer_player_seasons = await useServerAPI({
'class': 'cbb_transfer_player_season',
'function': 'read',
'arguments': {
'season': season,
}
});

const teams = await useServerAPI({
'class': 'team',
'function': 'read',
'arguments': {
'cbb': 1,
'cbb_d1': 1,
}
});

const player_id_x_cbb_transfer_player_season = {}
for (let cbb_transfer_player_season_id in cbb_transfer_player_seasons) {
player_id_x_cbb_transfer_player_season[cbb_transfer_player_seasons[cbb_transfer_player_season_id].player_id] = cbb_transfer_player_seasons[cbb_transfer_player_season_id];
}

for (let id in data) {
if (
!data[id].player_id ||
!(data[id].player_id in player_id_x_cbb_transfer_player_season)
) {
delete data[id];
} else {
data[id].committed = player_id_x_cbb_transfer_player_season[data[id].player_id].committed;
data[id].committed_team_id = player_id_x_cbb_transfer_player_season[data[id].player_id].committed_team_id;
data[id].committed_team_name = (data[id].committed_team_id in teams ? teams[data[id].committed_team_id].alt_name : '-');
}
}
}

return {
'data': data,
'generated': new Date().getTime(),
Expand Down
46 changes: 35 additions & 11 deletions app/cbb/ranking/ranking-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import TableSortLabel from '@mui/material/TableSortLabel';
import Paper from '@mui/material/Paper';
import Tooltip from '@mui/material/Tooltip';
import { visuallyHidden } from '@mui/utils';
import CheckIcon from '@mui/icons-material/Check';

import OptionPicker from '@/components/generic/OptionPicker';
import SeasonPicker from '@/components/generic/CBB/SeasonPicker';
Expand All @@ -41,6 +42,7 @@ import { useAppDispatch, useAppSelector } from '@/redux/hooks';
import { clearPositions, updateConferences, updatePositions } from '@/redux/features/display-slice';
import { getHeaderColumns } from './columns';
import { getRowsData } from './rows';
import AdditionalOptions from '@/components/generic/CBB/Ranking/AdditionalOptions';


// TODO Filter out people who play under x minutes?
Expand Down Expand Up @@ -80,6 +82,8 @@ const Ranking = (props) => {
const dispatch = useAppDispatch();
const conferences = useAppSelector(state => state.displayReducer.conferences);
const positions = useAppSelector(state => state.displayReducer.positions);
const hideCommitted = useAppSelector(state => state.rankingReducer.hideCommitted);
const hideUnderTwoMPG = useAppSelector(state => state.rankingReducer.hideUnderTwoMPG);

interface TableComponentsType {
Scroller: React.ComponentType<any>;
Expand Down Expand Up @@ -152,6 +156,7 @@ const Ranking = (props) => {
{'value': 'team', 'label': 'Team rankings'},
{'value': 'player', 'label': 'Player rankings'},
{'value': 'conference', 'label': 'Conference rankings'},
{'value': 'transfer', 'label': 'Transfer rankings'},
];

const handleRankView = (newRankView) => {
Expand Down Expand Up @@ -227,19 +232,21 @@ const Ranking = (props) => {
return ['composite_rank', 'name', 'team_name', 'efficiency_rating', 'offensive_rating', 'defensive_rating', 'player_efficiency_rating', 'minutes_per_game', 'points_per_game', 'usage_percentage', 'true_shooting_percentage'];
} else if (rankView === 'conference') {
return ['composite_rank', 'name', 'adjusted_efficiency_rating', 'elo', 'elo_sos', 'opponent_efficiency_rating', 'offensive_rating', 'defensive_rating', 'nonconfwins'];
} else if (rankView === 'transfer') {
return ['composite_rank', 'name', 'team_name', 'committed_team_name', 'efficiency_rating', 'offensive_rating', 'defensive_rating', 'player_efficiency_rating', 'minutes_per_game', 'points_per_game', 'usage_percentage', 'true_shooting_percentage'];
}
} else if (view === 'offense') {
if (rankView === 'team') {
return ['composite_rank', 'name', 'offensive_rating', 'points', 'field_goal_percentage', 'two_point_field_goal_percentage', 'three_point_field_goal_percentage', 'free_throw_percentage', 'offensive_rebounds', 'assists', 'turnovers', 'possessions', 'pace'];
} else if (rankView === 'player') {
} else if (rankView === 'player' || rankView === 'transfer') {
return ['composite_rank', 'name', 'offensive_rating', 'points_per_game', 'field_goal_percentage', 'two_point_field_goal_percentage', 'three_point_field_goal_percentage', 'free_throw_percentage', 'offensive_rebounds_per_game', 'assists_per_game', 'turnovers_per_game', 'turnover_percentage'];
} else if (rankView === 'conference') {
return ['composite_rank', 'name','offensive_rating', 'points', 'field_goal_percentage', 'two_point_field_goal_percentage', 'three_point_field_goal_percentage', 'free_throw_percentage', 'offensive_rebounds', 'assists', 'turnovers', 'possessions', 'pace'];
}
} else if (view === 'defense') {
if (rankView === 'team') {
return ['composite_rank', 'name', 'defensive_rating', 'defensive_rebounds', 'steals', 'blocks', 'opponent_points', 'opponent_field_goal_percentage', 'opponent_two_point_field_goal_percentage', 'opponent_three_point_field_goal_percentage', 'fouls'];
} else if (rankView === 'player') {
} else if (rankView === 'player' || rankView === 'transfer') {
return ['composite_rank', 'name', 'defensive_rating', 'defensive_rebounds_per_game', 'steals_per_game', 'blocks_per_game', 'fouls_per_game', 'defensive_rebound_percentage', 'steal_percentage', 'block_percentage'];
} else if (rankView === 'conference') {
return ['composite_rank', 'name', 'defensive_rating', 'defensive_rebounds', 'steals', 'blocks', 'opponent_points', 'opponent_field_goal_percentage', 'opponent_two_point_field_goal_percentage', 'opponent_three_point_field_goal_percentage', 'fouls'];
Expand All @@ -253,11 +260,11 @@ const Ranking = (props) => {

const headCells = getHeaderColumns({rankView});

const rowsData = getRowsData({ data, rankView, conferences, positions });
const rowsData = getRowsData({ data, rankView, conferences, positions, hideCommitted, hideUnderTwoMPG });
let rows = rowsData.rows;
let lastUpdated = rowsData.lastUpdated;

const row_length_before_filter = Object.keys(data).length;
const row_length_before_filter = (rankView === 'transfer' ? 5300 : Object.keys(data).length);
const allRows = rows;

if (filteredRows !== null && filteredRows !== false) {
Expand Down Expand Up @@ -337,7 +344,7 @@ const Ranking = (props) => {
TableRow: React.forwardRef<HTMLTableRowElement>((props, ref) => {
return (
<StyledTableRow {...props} ref={ref} onClick={() => {
if (rankView === 'player' && (props as any).item.player_id) {
if ((rankView === 'player' || rankView === 'transfer') && (props as any).item.player_id) {
handlePlayer((props as any).item.player_id);
} else if (rankView === 'team' && (props as any).item.team_id) {
handleTeam((props as any).item.team_id);
Expand Down Expand Up @@ -501,6 +508,8 @@ const Ranking = (props) => {
tableCells.push(<TableCell key = {i} sx = {Object.assign({}, tdStyle, rankCellStyle)}>{row[columns[i]]}</TableCell>);
} else if (columns[i] === 'conf') {
tableCells.push(<TableCell key = {i} sx = {Object.assign({}, tdStyle, conferenceCellStyle)}>{row[columns[i]]}</TableCell>);
} else if (columns[i] === 'committed') {
tableCells.push(<TableCell key = {i} sx = {tdStyle}>{row[columns[i]] === 1 ? <CheckIcon fontSize='small' color = 'success' /> : '-'}</TableCell>);
} else {
tableCells.push(<TableCell key = {i} sx = {tdStyle}>{row[columns[i]] !== null ? row[columns[i]] : '-'}{row[columns[i] + '_rank'] && row[columns[i]] !== null ? <RankSpan rank = {row[columns[i] + '_rank']} useOrdinal = {(rankView !== 'player')} max = {row_length_before_filter} /> : ''}</TableCell>);
}
Expand Down Expand Up @@ -541,6 +550,20 @@ const Ranking = (props) => {
setFilteredRows(filteredRows);
};

const getLastUpdated = (): string => {
if (!lastUpdated) {
return '';
}

if (rankView === 'transfer') {
let date = moment();
date = date.subtract(1, 'days');
return date.format('MMMM Do YYYY');
}

return moment(lastUpdated.split('T')[0]).format('MMMM Do YYYY');
};


return (
<div>
Expand All @@ -554,19 +577,20 @@ const Ranking = (props) => {
<OptionPicker title = 'View' options = {rankViewOptions} selected = {rankView} actionHandler = {handleRankView} />
<SeasonPicker selected = {season} actionHandler = {handleSeason} />
</div>
<Typography variant = 'h5'>College basketball rankings.</Typography>
{lastUpdated ? <Typography color="text.secondary" variant = 'body1' style = {{'fontStyle': 'italic'}}>Last updated: {moment(lastUpdated.split('T')[0]).format('MMMM Do YYYY')}</Typography> : ''}
<Typography variant = 'h5'>{'College basketball' + (rankView === 'transfer' ? ' transfers.' : 'rankings.')}</Typography>
{lastUpdated ? <Typography color="text.secondary" variant = 'body1' style = {{'fontStyle': 'italic'}}>{'Last updated: ' + getLastUpdated()}</Typography> : ''}
<div style = {{'display': 'flex', 'justifyContent': 'center', 'flexWrap': 'wrap'}}>
<Chip sx = {{'margin': '5px'}} label='Composite' variant={view !== 'composite' ? 'outlined' : 'filled'} color={view !== 'composite' ? 'primary' : 'success'} onClick={() => handleRankingView('composite')} />
<Chip sx = {{'margin': '5px'}} label='Offense' variant={view !== 'offense' ? 'outlined' : 'filled'} color={view !== 'offense' ? 'primary' : 'success'} onClick={() => handleRankingView('offense')} />
<Chip sx = {{'margin': '5px'}} label='Defense' variant={view !== 'defense' ? 'outlined' : 'filled'} color={view !== 'defense' ? 'primary' : 'success'} onClick={() => handleRankingView('defense')} />
<Chip sx = {{'margin': '5px'}} label='Custom' variant={view !== 'custom' ? 'outlined' : 'filled'} color={view !== 'custom' ? 'primary' : 'success'} onClick={() => {setCustomColumnsOpen(true)}} />
<ColumnPicker key = {rankView} options = {headCells} open = {customColumnsOpen} selected = {customColumns} saveHandler = {handlCustomColumnsSave} closeHandler = {handlCustomColumnsExit} />
</div>
<div style = {{'display': 'flex', 'justifyContent': 'space-between', 'alignItems': 'center', 'marginTop': '10px'}}>
<div style={{'display': 'flex'}}>
{rankView === 'player' || rankView === 'team' ? <ConferencePicker /> : ''}
{rankView === 'player' ? <PositionPicker selected = {positions} /> : ''}
<div style = {{'display': 'flex', 'justifyContent': 'space-between', 'alignItems': 'baseline', 'marginTop': '10px'}}>
<div style={{'display': 'flex', 'alignItems': 'center',}}>
{rankView === 'player' || rankView === 'transfer' ? <AdditionalOptions view = {rankView} /> : ''}
{rankView === 'player' || rankView === 'team' || rankView === 'transfer' ? <ConferencePicker /> : ''}
{rankView === 'player' || rankView === 'transfer' ? <PositionPicker selected = {positions} /> : ''}
</div>
<RankSearch rows = {allRows} callback = {handleSearch} />
</div>
Expand Down
15 changes: 13 additions & 2 deletions app/cbb/ranking/rows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ interface rowDatatype {
opponent_efficiency_rating_rank: number;
};

export const getRowsData = ({ data, rankView, conferences, positions }) => {
export const getRowsData = ({ data, rankView, conferences, positions, hideCommitted, hideUnderTwoMPG }) => {
let rows: rowDatatype[] = [];
let lastUpdated: string | null = null;

Expand All @@ -163,6 +163,17 @@ export const getRowsData = ({ data, rankView, conferences, positions }) => {
continue;
}

// transfers
if (hideCommitted && +row.committed === 1) {
continue;
}

if (rankView === 'player' || rankView === 'transfer') {
if (hideUnderTwoMPG && row.minutes_per_game < 2) {
continue;
}
}

if (rankView === 'team') {
if (
row.last_ranking &&
Expand Down Expand Up @@ -327,7 +338,7 @@ export const getRowsData = ({ data, rankView, conferences, positions }) => {
'opponent_efficiency_rating_rank': (row.cbb_statistic_ranking && row.cbb_statistic_ranking.opponent_efficiency_rating_rank) || null,
'elo_sos_rank': (row.cbb_statistic_ranking && row.cbb_statistic_ranking.elo_sos_rank) || null,
});
} else if (rankView === 'player') {
} else if (rankView === 'player' || rankView === 'transfer') {
if (
!lastUpdated ||
lastUpdated < row.date_of_rank
Expand Down

0 comments on commit 5ca83d8

Please sign in to comment.