Skip to content

Commit

Permalink
Merge pull request #106 from Alex-At-Home/lineups_as_badges
Browse files Browse the repository at this point in the history
[#102] Lineup decoration
  • Loading branch information
Alex-At-Home authored Jul 16, 2020
2 parents ae4e4fd + 7649ab5 commit fb651ac
Show file tree
Hide file tree
Showing 18 changed files with 10,633 additions and 190 deletions.
11 changes: 8 additions & 3 deletions src/components/GenericTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ export class GenericTableOps {
static readonly defaultFormatter = (val: any) => "" + val;
static readonly htmlFormatter = (val: React.ReactNode) => val;
static readonly intFormatter = (val: any) => "" + (val.value as number).toFixed(0);
static readonly percentFormatter = (val: any) => ((val.value as number)*100.0).toFixed(1); //(no % it's too ugly)
static readonly percentFormatter = (val: any) => {
return (val.value >= 1) ?
((val.value as number)*100.0).toFixed(0) //(remove the .0 in the 100% case)
: ((val.value as number)*100.0).toFixed(1); //(no % it's too ugly)
}
static readonly pointsFormatter = (val: any) => (val.value as number).toFixed(1);
static readonly defaultCellMeta = (key: string, value: any) => "";
static readonly defaultColorPicker = (val: any, cellMeta: string) => undefined;
Expand Down Expand Up @@ -130,9 +134,10 @@ export class GenericTableOps {
colName: string, toolTip: string,
rowSpan: (key: string) => number = GenericTableOps.defaultRowSpanCalculator,
className: string = "",
colFormatterOverride: (val: any) => string | React.ReactNode = GenericTableOps.defaultFormatter
colFormatterOverride: (val: any) => string | React.ReactNode = GenericTableOps.defaultFormatter,
widthOverride: number = 8
) {
return new GenericTableColProps(colName, toolTip, 8, true,
return new GenericTableColProps(colName, toolTip, widthOverride, true,
colFormatterOverride, GenericTableOps.defaultColorPicker,
rowSpan, undefined, className
);
Expand Down
2 changes: 2 additions & 0 deletions src/components/LineupFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const LineupFilter: React.FunctionComponent<Props> = ({onStats, startingState, o
luck: startLuck,
lineupLuck: startLineupLuck, showLineupLuckDiags: startShowLineupLuckDiags,
// Filters etc
decorate: startDecorate,
showTotal: startShowTotal,
maxTableSize: startMaxTableSize,
minPoss: startMinPoss,
Expand Down Expand Up @@ -75,6 +76,7 @@ const LineupFilter: React.FunctionComponent<Props> = ({onStats, startingState, o
luck: startLuck,
lineupLuck: startLineupLuck, showLineupLuckDiags: startShowLineupLuckDiags,
// Filters etc
decorate: startDecorate,
showTotal: startShowTotal,
maxTableSize: startMaxTableSize,
minPoss: startMinPoss,
Expand Down
50 changes: 43 additions & 7 deletions src/components/LineupStatsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import GenericTogglingMenuItem from './shared/GenericTogglingMenuItem';
import LuckAdjDiagView from './diags/LuckAdjDiagView';

// Util imports
import { LineupDisplayUtils } from "../utils/stats/LineupDisplayUtils";
import { RatingUtils } from "../utils/stats/RatingUtils";
import { LineupUtils } from "../utils/stats/LineupUtils";
import { CbbColors } from "../utils/CbbColors";
import { CommonTableDefs } from "../utils/CommonTableDefs";
Expand Down Expand Up @@ -89,6 +91,11 @@ const LineupStatsTable: React.FunctionComponent<Props> = ({lineupStats, teamStat
/** Whether we are showing the luck config modal */
const [ showLuckConfig, setShowLuckConfig ] = useState(false);

/** Whether to badge/colorize the lineups */
const [ decorateLineups, setDecorateLineups ] = useState(_.isNil(startingState.decorate) ?
ParamDefaults.defaultLineupDecorate : startingState.decorate
);

// (slight delay when typing into the filter to make it more responsive)
const [ timeoutId, setTimeoutId ] = useState(-1);
const [ tmpFilterStr, setTmpFilterStr ] = useState(filterStr);
Expand All @@ -104,19 +111,21 @@ const LineupStatsTable: React.FunctionComponent<Props> = ({lineupStats, teamStat
lineupLuck: adjustForLuck,
showLineupLuckDiags: showLuckAdjDiags,
// Misc filters
decorate: decorateLineups,
showTotal: showTotals,
minPoss: minPoss,
maxTableSize: maxTableSize,
sortBy: sortBy,
filter: filterStr
}).omit(_.flatten([
(decorateLineups == ParamDefaults.defaultLineupDecorate) ? [ 'decorate' ] : [],
(showTotals == ParamDefaults.defaultLineupShowTotal) ? [ 'showTotal' ] : [],
_.isEqual(luckConfig, ParamDefaults.defaultLuckConfig) ? [ 'luck' ] : [],
!adjustForLuck ? [ 'lineupLuck' ] : [],
(showLuckAdjDiags == ParamDefaults.defaultOnOffLuckDiagMode) ? [ 'showLineupLuckDiags' ] : []
])).value();
onChangeState(newState);
}, [ showTotals, minPoss, maxTableSize, sortBy, filterStr,
}, [ decorateLineups, showTotals, minPoss, maxTableSize, sortBy, filterStr,
luckConfig, adjustForLuck, showLuckAdjDiags ]);

// 3] Utils
Expand All @@ -126,13 +135,28 @@ const LineupStatsTable: React.FunctionComponent<Props> = ({lineupStats, teamStat
const genderYearLookup = `${startingState.gender}_${startingState.year}`;
const avgEfficiency = efficiencyAverages[genderYearLookup] || efficiencyAverages.fallback;

/** Need baseline player info for tooltip view/lineup decoration */
const baselinePlayerInfo = _.fromPairs(
(rosterStats.baseline || []).map((mutableP: any) => {
// Add ORtg to lineup stats:
const [ oRtg, adjORtg, oRtgDiag ] = RatingUtils.buildORtg(mutableP, avgEfficiency, false);
const [ dRtg, adjDRtg, dRtgDiag ] = RatingUtils.buildDRtg(mutableP, avgEfficiency, false);
mutableP.off_rtg = oRtg;
mutableP.off_adj_rtg = adjORtg;
mutableP.def_rtg = dRtg;
mutableP.def_adj_rtg = adjDRtg;

return [ mutableP.key, mutableP ];
})
);

// The luck baseline can either be the user-selecteed baseline or the entire season
const [ baseOrSeasonTeamStats, baseOrSeason3PMap ] = (() => {
if (adjustForLuck) {
switch (luckConfig.base) {
case "baseline":
return [
teamStats.baseline, _.fromPairs((rosterStats.baseline || []).map((p: any) => [ p.key, p ]))
teamStats.baseline, baselinePlayerInfo
];
default: //("season")
return [
Expand Down Expand Up @@ -214,21 +238,28 @@ const LineupStatsTable: React.FunctionComponent<Props> = ({lineupStats, teamStat
const sortedCodesAndIds = (lineup.key == totalLineupId) ? undefined :
PositionUtils.orderLineup(codesAndIds, positionFromPlayerKey, teamSeasonLookup);

const perLineupPlayerMap = _.fromPairs(codesAndIds.map((cid: { code: string, id: string }) => {
const perLineupPlayerLuckMap = _.fromPairs(codesAndIds.map((cid: { code: string, id: string }) => {
return [ cid.id, baseOrSeason3PMap[cid.id] ];
}));
const luckAdj = (adjustForLuck && lineup?.doc_count) ? [
LuckUtils.calcOffTeamLuckAdj(lineup, rosterStats.baseline || [], baseOrSeasonTeamStats, perLineupPlayerMap, avgEfficiency),
LuckUtils.calcOffTeamLuckAdj(
lineup, rosterStats.baseline || [], baseOrSeasonTeamStats, perLineupPlayerLuckMap, avgEfficiency
),
LuckUtils.calcDefTeamLuckAdj(lineup, baseOrSeasonTeamStats, avgEfficiency),
] as [OffLuckAdjustmentDiags, DefLuckAdjustmentDiags] : undefined;

if (lineup?.doc_count) {
LuckUtils.injectLuck(lineup, luckAdj?.[0], luckAdj?.[1]);
}

const perLineupBaselinePlayerMap = _.fromPairs(codesAndIds.map((cid: { code: string, id: string }) => {
return [ cid.id, baselinePlayerInfo[cid.id] || {} ];
})) as Record<string, Record<string, any>>;
const title = sortedCodesAndIds ?
sortedCodesAndIds.map((cid: { code: string, id: string}) => cid.code).join(" / ") :
"Weighted Total";
LineupDisplayUtils.buildDecoratedLineup(
lineup.key, sortedCodesAndIds, perLineupBaselinePlayerMap, positionFromPlayerKey, "off_adj_rtg", decorateLineups
) : "Weighted Total";

const stats = { off_title: title, def_title: "", ...lineup };
return _.flatten([
[ GenericTableOps.buildDataRow(stats, offPrefixFn, offCellMetaFn) ],
Expand Down Expand Up @@ -361,14 +392,19 @@ const LineupStatsTable: React.FunctionComponent<Props> = ({lineupStats, teamStat
<Form.Control
onKeyUp={onFilterChange}
onChange={onFilterChange}
placeholder = "eg Player1Code=PG/Player2FirstName/-Player3Surname/Player4Name=4+5"
placeholder = "eg Player1Code=PG;Player2FirstName;-Player3Surname;Player4Name=4+5"
value={tmpFilterStr}
/>
</InputGroup>
</Form.Group>
<Col sm="3"/>
<Form.Group as={Col} sm="1">
<GenericTogglingMenu>
<GenericTogglingMenuItem
text="Decorate Lineups"
truthVal={decorateLineups}
onSelect={() => setDecorateLineups(!decorateLineups)}
/>
<GenericTogglingMenuItem
text="Show Weighted Combo of All Lineups"
truthVal={showTotals}
Expand Down
26 changes: 25 additions & 1 deletion src/components/__tests__/LineupStatsTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("LineupStatsTable", () => {
<LineupStatsTable
lineupStats={testData}
rosterStats={{}}
teamStats={{}}
teamStats={{ on:{}, off: {}, baseline: {}, global: {}, onOffMode: true }}
startingState={{}}
onChangeState={dummyChangeStateCallback}
/>
Expand Down Expand Up @@ -50,4 +50,28 @@ describe("LineupStatsTable", () => {
);
expect(toJson(wrapper)).toMatchSnapshot();
});
test("LineupStatsTable - should create snapshot (with individual data - plain, luck, luck diags)", () => {
const testData = {
lineups: sampleLineupStatsResponse.responses[0].aggregations.lineups.buckets
};
const teamData = _.merge(
sampleTeamStatsResponse.aggregations.tri_filter.buckets,
{ global: {}, onOffMode: true }
);
const playerData = {
baseline: samplePlayerStatsResponse.aggregations.tri_filter.buckets.on.player.buckets,
global: samplePlayerStatsResponse.aggregations.tri_filter.buckets.baseline.player.buckets
};
const dummyChangeStateCallback = (stats: LineupFilterParams) => {};
const wrapper = shallow(
<LineupStatsTable
lineupStats={testData}
teamStats={teamData}
rosterStats={playerData}
startingState={{ decorate: false, lineupLuck: true, showLineupLuckDiags: true }}
onChangeState={dummyChangeStateCallback}
/>
);
expect(toJson(wrapper)).toMatchSnapshot();
});
});
Loading

1 comment on commit fb651ac

@vercel
Copy link

@vercel vercel bot commented on fb651ac Jul 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.