@firestone-hs/bgs-global-stats 1.0.45 → 1.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,8 @@ const mergeStatsForSingleHero = (stats, allCards) => {
30
30
  const result = {
31
31
  heroCardId: ref.heroCardId,
32
32
  dataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),
33
+ totalOffered: stats.map((stat) => { var _a; return (_a = stat.totalOffered) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
34
+ totalPicked: stats.map((stat) => { var _a; return (_a = stat.totalPicked) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
33
35
  averagePosition: (0, util_functions_1.round)(averagePosition),
34
36
  standardDeviation: (0, util_functions_1.round)(standardDeviation),
35
37
  standardDeviationOfTheMean: (0, util_functions_1.round)(standardDeviationOfTheMean),
@@ -60,11 +62,13 @@ const mergeTribeStats = (stats, refAveragePosition) => {
60
62
  dataPointsOnMissingTribe: tribeStats
61
63
  .map((stat) => stat.dataPointsOnMissingTribe)
62
64
  .reduce((a, b) => a + b, 0),
65
+ totalOffered: tribeStats.map((stat) => { var _a; return (_a = stat.totalOffered) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
66
+ totalPicked: tribeStats.map((stat) => { var _a; return (_a = stat.totalPicked) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
63
67
  averagePosition: (0, util_functions_1.round)(averagePosition),
64
68
  averagePositionWithoutTribe: (0, util_functions_1.round)(averagePositionWithoutTribe),
65
69
  refAveragePosition: refAveragePosition,
66
70
  impactAveragePosition: (0, util_functions_1.round)(averagePosition - refAveragePosition),
67
- impactAveragePositionVsMissingTribe: (0, util_functions_1.round)(averagePositionWithoutTribe - refAveragePosition),
71
+ impactAveragePositionVsMissingTribe: (0, util_functions_1.round)(averagePosition - averagePositionWithoutTribe),
68
72
  };
69
73
  });
70
74
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { round } from '../../../common/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, BgsHeroTribeStat, MmrPercentileFilter } from '../../../models';\r\n\r\nexport const mergeStats = (\r\n\thourlyData: readonly BgsHeroStatsV2[],\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst allStats: readonly BgsGlobalHeroStat[] = hourlyData\r\n\t\t.flatMap((data) => data.heroStats)\r\n\t\t.filter((stat) => stat.mmrPercentile === mmrPercentile)\r\n\t\t.filter((stat) => stat.heroCardId !== CardIds.BaconphheroHeroic);\r\n\t// console.debug('allStats', mmrPercentile, allStats.length, '/', hourlyData.length);\r\n\tconst groupedByHero: {\r\n\t\t[heroCardId: string]: readonly BgsGlobalHeroStat[];\r\n\t} = groupByFunction((stat: BgsGlobalHeroStat) => stat.heroCardId)(allStats);\r\n\tconst result: readonly BgsGlobalHeroStat[] = Object.values(groupedByHero).map((stats) =>\r\n\t\tmergeStatsForSingleHero(stats, allCards),\r\n\t);\r\n\tconst maxDataPoints = Math.max(...result.map((r) => r.dataPoints));\r\n\tconst filtered = result.filter((r) => r.dataPoints > maxDataPoints / 50);\r\n\treturn filtered;\r\n};\r\n\r\nconst mergeStatsForSingleHero = (stats: readonly BgsGlobalHeroStat[], allCards: AllCardsService): BgsGlobalHeroStat => {\r\n\tconst ref = stats[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_010';\r\n\r\n\tconst totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst averagePosition = totalWeightedAverage / totalDataPoints;\r\n\r\n\tconst totalWeightedVariance = stats\r\n\t\t.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)\r\n\t\t.reduce((a, b) => a + b, 0);\r\n\tconst overallVariance = totalWeightedVariance / totalDataPoints;\r\n\tconst standardDeviation = Math.sqrt(overallVariance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);\r\n\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistribution: mergePlacementDistributions(stats),\r\n\t\twarbandStats: mergeWarbandStats(stats),\r\n\t\tcombatWinrate: mergeCombatWinrate(stats, debug),\r\n\t\ttribeStats: mergeTribeStats(stats, averagePosition).filter(\r\n\t\t\t(s) => s.dataPointsOnMissingTribe > s.dataPoints / 20,\r\n\t\t),\r\n\t\tanomalyStats: [], // mergeAnomalyStats(stats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst mergeTribeStats = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst allTribeStats = stats.flatMap((stat) => stat.tribeStats);\r\n\tconst uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));\r\n\tconst result: BgsHeroTribeStat[] = [...uniqueTribes].map((tribe) => {\r\n\t\tconst tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);\r\n\t\tconst totalStatDataPoints = tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst averagePosition =\r\n\t\t\ttribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t\ttotalStatDataPoints;\r\n\t\tconst averagePositionWithoutTribe =\r\n\t\t\ttribeStats\r\n\t\t\t\t.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePosition =\r\n\t\t// \ttribeStats.map((stat) => stat.impactAveragePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t// \ttotalStatDataPoints;\r\n\t\t// const impactAveragePositionVsMissingTribe =\r\n\t\t// \ttribeStats\r\n\t\t// \t\t.map((stat) => stat.impactAveragePositionVsMissingTribe * stat.dataPoints)\r\n\t\t// \t\t.reduce((a, b) => a + b, 0) / totalStatDataPoints;\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: totalStatDataPoints,\r\n\t\t\tdataPointsOnMissingTribe: tribeStats\r\n\t\t\t\t.map((stat) => stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0),\r\n\t\t\taveragePosition: round(averagePosition),\r\n\t\t\taveragePositionWithoutTribe: round(averagePositionWithoutTribe),\r\n\t\t\trefAveragePosition: refAveragePosition,\r\n\t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n\t\t\t// impactAveragePositionDebug: round(impactAveragePosition),\r\n\t\t\timpactAveragePositionVsMissingTribe: round(averagePositionWithoutTribe - refAveragePosition),\r\n\t\t};\r\n\t});\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributions = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; percentage: number }[] => {\r\n\tconst rawMerge = mergePlacementDistributionsRaw(stats);\r\n\tconst result: { rank: number; percentage: number }[] = [];\r\n\tconst totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawMerge.find((d) => d.rank === i)?.totalMatches ?? 0;\r\n\t\tresult.push({ rank: i, percentage: round((100 * total) / totalDataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributionsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst rawStats = stats.map((stat) => stat.placementDistributionRaw);\r\n\t// console.debug(\r\n\t// \t'will merge placement distributions',\r\n\t// \tstats[0].heroCardId,\r\n\t// \tstats[0],\r\n\t// \trawStats.length,\r\n\t// \trawStats.filter((s) => !!s).length,\r\n\t// );\r\n\t// Legacy, can be removed after 2024-01-31\r\n\t// const pStats = stats\r\n\t// \t.filter((stat) => stat.placementDistribution?.length)\r\n\t// \t.map((stat) =>\r\n\t// \t\tstat.placementDistribution.map((s) => ({\r\n\t// \t\t\trank: s.rank,\r\n\t// \t\t\ttotalMatches: s.percentage * stat.dataPoints,\r\n\t// \t\t})),\r\n\t// \t);\r\n\t// const allRawStats = [...rawStats, ...pStats].filter((s) => !!s);\r\n\tconst result: { rank: number; totalMatches: number }[] = [];\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawStats\r\n\t\t\t.map((d) => d?.find((info) => info.rank === i)?.totalMatches ?? 0)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ rank: i, totalMatches: total });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStats = (stats: readonly BgsGlobalHeroStat[]): readonly { turn: number; averageStats: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);\r\n\tconst result: { turn: number; averageStats: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statsForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, averageStats: round(statsForTurn.totalStats / statsForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStatsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalStats: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!s?.length);\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalStats: number = rawStatsForTurn.map((d) => d?.totalStats ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });\r\n\t}\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst mergeCombatWinrate = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): readonly { turn: number; winrate: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);\r\n\tconst result: { turn: number; winrate: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, winrate: round(statForTurn.totalWinrate / statForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeCombatWinrateRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalWinrate: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!stat?.length);\r\n\t// debug && console.log('rawStats', rawStats?.length, rawStats);\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\t// debug && console.log('maxTurn', maxTurn);\r\n\tfor (let i = 0; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalWinrate: number = rawStatsForTurn.map((d) => d?.totalWinrate ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });\r\n\t}\r\n\t// debug && console.log('result', result);\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst getMaxTurn = (rawStats: readonly { turn: number }[]): number => {\r\n\tif (!rawStats?.length) {\r\n\t\treturn 0;\r\n\t}\r\n\treturn Math.max(...rawStats.map((stat) => stat.turn));\r\n};\r\n"]}
1
+ {"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { round } from '../../../common/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, BgsHeroTribeStat, MmrPercentileFilter } from '../../../models';\r\n\r\nexport const mergeStats = (\r\n\thourlyData: readonly BgsHeroStatsV2[],\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst allStats: readonly BgsGlobalHeroStat[] = hourlyData\r\n\t\t.flatMap((data) => data.heroStats)\r\n\t\t.filter((stat) => stat.mmrPercentile === mmrPercentile)\r\n\t\t.filter((stat) => stat.heroCardId !== CardIds.BaconphheroHeroic);\r\n\t// console.debug('allStats', mmrPercentile, allStats.length, '/', hourlyData.length);\r\n\tconst groupedByHero: {\r\n\t\t[heroCardId: string]: readonly BgsGlobalHeroStat[];\r\n\t} = groupByFunction((stat: BgsGlobalHeroStat) => stat.heroCardId)(allStats);\r\n\tconst result: readonly BgsGlobalHeroStat[] = Object.values(groupedByHero).map((stats) =>\r\n\t\tmergeStatsForSingleHero(stats, allCards),\r\n\t);\r\n\tconst maxDataPoints = Math.max(...result.map((r) => r.dataPoints));\r\n\tconst filtered = result.filter((r) => r.dataPoints > maxDataPoints / 50);\r\n\treturn filtered;\r\n};\r\n\r\nconst mergeStatsForSingleHero = (stats: readonly BgsGlobalHeroStat[], allCards: AllCardsService): BgsGlobalHeroStat => {\r\n\tconst ref = stats[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_010';\r\n\r\n\tconst totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst averagePosition = totalWeightedAverage / totalDataPoints;\r\n\r\n\tconst totalWeightedVariance = stats\r\n\t\t.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)\r\n\t\t.reduce((a, b) => a + b, 0);\r\n\tconst overallVariance = totalWeightedVariance / totalDataPoints;\r\n\tconst standardDeviation = Math.sqrt(overallVariance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);\r\n\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\ttotalOffered: stats.map((stat) => stat.totalOffered ?? 0).reduce((a, b) => a + b, 0),\r\n\t\ttotalPicked: stats.map((stat) => stat.totalPicked ?? 0).reduce((a, b) => a + b, 0),\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistribution: mergePlacementDistributions(stats),\r\n\t\twarbandStats: mergeWarbandStats(stats),\r\n\t\tcombatWinrate: mergeCombatWinrate(stats, debug),\r\n\t\ttribeStats: mergeTribeStats(stats, averagePosition).filter(\r\n\t\t\t(s) => s.dataPointsOnMissingTribe > s.dataPoints / 20,\r\n\t\t),\r\n\t\tanomalyStats: [], // mergeAnomalyStats(stats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst mergeTribeStats = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst allTribeStats = stats.flatMap((stat) => stat.tribeStats);\r\n\tconst uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));\r\n\tconst result: BgsHeroTribeStat[] = [...uniqueTribes].map((tribe) => {\r\n\t\tconst tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);\r\n\t\tconst totalStatDataPoints = tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst averagePosition =\r\n\t\t\ttribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t\ttotalStatDataPoints;\r\n\t\tconst averagePositionWithoutTribe =\r\n\t\t\ttribeStats\r\n\t\t\t\t.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePosition =\r\n\t\t// \ttribeStats.map((stat) => stat.impactAveragePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t// \ttotalStatDataPoints;\r\n\t\t// const impactAveragePositionVsMissingTribe =\r\n\t\t// \ttribeStats\r\n\t\t// \t\t.map((stat) => stat.impactAveragePositionVsMissingTribe * stat.dataPoints)\r\n\t\t// \t\t.reduce((a, b) => a + b, 0) / totalStatDataPoints;\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: totalStatDataPoints,\r\n\t\t\tdataPointsOnMissingTribe: tribeStats\r\n\t\t\t\t.map((stat) => stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0),\r\n\t\t\ttotalOffered: tribeStats.map((stat) => stat.totalOffered ?? 0).reduce((a, b) => a + b, 0),\r\n\t\t\ttotalPicked: tribeStats.map((stat) => stat.totalPicked ?? 0).reduce((a, b) => a + b, 0),\r\n\t\t\taveragePosition: round(averagePosition),\r\n\t\t\taveragePositionWithoutTribe: round(averagePositionWithoutTribe),\r\n\t\t\trefAveragePosition: refAveragePosition,\r\n\t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n\t\t\t// impactAveragePositionDebug: round(impactAveragePosition),\r\n\t\t\timpactAveragePositionVsMissingTribe: round(averagePosition - averagePositionWithoutTribe),\r\n\t\t};\r\n\t});\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributions = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; percentage: number }[] => {\r\n\tconst rawMerge = mergePlacementDistributionsRaw(stats);\r\n\tconst result: { rank: number; percentage: number }[] = [];\r\n\tconst totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawMerge.find((d) => d.rank === i)?.totalMatches ?? 0;\r\n\t\tresult.push({ rank: i, percentage: round((100 * total) / totalDataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributionsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst rawStats = stats.map((stat) => stat.placementDistributionRaw);\r\n\t// console.debug(\r\n\t// \t'will merge placement distributions',\r\n\t// \tstats[0].heroCardId,\r\n\t// \tstats[0],\r\n\t// \trawStats.length,\r\n\t// \trawStats.filter((s) => !!s).length,\r\n\t// );\r\n\t// Legacy, can be removed after 2024-01-31\r\n\t// const pStats = stats\r\n\t// \t.filter((stat) => stat.placementDistribution?.length)\r\n\t// \t.map((stat) =>\r\n\t// \t\tstat.placementDistribution.map((s) => ({\r\n\t// \t\t\trank: s.rank,\r\n\t// \t\t\ttotalMatches: s.percentage * stat.dataPoints,\r\n\t// \t\t})),\r\n\t// \t);\r\n\t// const allRawStats = [...rawStats, ...pStats].filter((s) => !!s);\r\n\tconst result: { rank: number; totalMatches: number }[] = [];\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawStats\r\n\t\t\t.map((d) => d?.find((info) => info.rank === i)?.totalMatches ?? 0)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ rank: i, totalMatches: total });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStats = (stats: readonly BgsGlobalHeroStat[]): readonly { turn: number; averageStats: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);\r\n\tconst result: { turn: number; averageStats: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statsForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, averageStats: round(statsForTurn.totalStats / statsForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStatsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalStats: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!s?.length);\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalStats: number = rawStatsForTurn.map((d) => d?.totalStats ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });\r\n\t}\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst mergeCombatWinrate = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): readonly { turn: number; winrate: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);\r\n\tconst result: { turn: number; winrate: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, winrate: round(statForTurn.totalWinrate / statForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeCombatWinrateRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalWinrate: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!stat?.length);\r\n\t// debug && console.log('rawStats', rawStats?.length, rawStats);\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\t// debug && console.log('maxTurn', maxTurn);\r\n\tfor (let i = 0; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalWinrate: number = rawStatsForTurn.map((d) => d?.totalWinrate ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });\r\n\t}\r\n\t// debug && console.log('result', result);\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst getMaxTurn = (rawStats: readonly { turn: number }[]): number => {\r\n\tif (!rawStats?.length) {\r\n\t\treturn 0;\r\n\t}\r\n\treturn Math.max(...rawStats.map((stat) => stat.turn));\r\n};\r\n"]}
@@ -6,27 +6,32 @@ const util_functions_1 = require("../../common/util-functions");
6
6
  const builders_1 = require("./builders");
7
7
  const utils_1 = require("./utils");
8
8
  const buildHeroStatsForMmr = (rows, allCards) => {
9
- const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((row) => (0, util_functions_1.normalizeHeroCardId)(row.heroCardId, allCards))(rows);
10
- return Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));
9
+ const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((row) => row.heroCardId)(rows);
10
+ return Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data, rows));
11
11
  };
12
12
  exports.buildHeroStatsForMmr = buildHeroStatsForMmr;
13
- const buildStatsForSingleHero = (rows) => {
14
- const ref = rows[0];
15
- const averagePosition = average(rows.map((r) => r.playerRank));
16
- const placementDistribution = (0, utils_1.buildPlacementDistribution)(rows);
17
- const rawCombatWinrates = (0, builders_1.buildCombatWinrate)(rows);
18
- const rawWarbandStats = (0, builders_1.buildWarbandStats)(rows);
19
- const allRanks = rows.map((r) => r.playerRank);
13
+ const buildStatsForSingleHero = (rowsForHero, allRows) => {
14
+ const ref = rowsForHero[0];
15
+ const offeredRows = allRows.filter((r) => r.heroesOptionsExpanded.includes(rowsForHero[0].heroCardId));
16
+ const totalOffered = offeredRows.length;
17
+ const totalPicked = offeredRows.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;
18
+ const averagePosition = average(rowsForHero.map((r) => r.playerRank));
19
+ const placementDistribution = (0, utils_1.buildPlacementDistribution)(rowsForHero);
20
+ const rawCombatWinrates = (0, builders_1.buildCombatWinrate)(rowsForHero);
21
+ const rawWarbandStats = (0, builders_1.buildWarbandStats)(rowsForHero);
22
+ const allRanks = rowsForHero.map((r) => r.playerRank);
20
23
  const allDeviations = allRanks.map((r) => averagePosition - r);
21
24
  const squareDeviations = allDeviations.map((d) => Math.pow(d, 2));
22
25
  const sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);
23
- const variance = sumOfSquares / rows.length;
26
+ const variance = sumOfSquares / rowsForHero.length;
24
27
  const standardDeviation = Math.sqrt(variance);
25
- const standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);
26
- const tribeStats = buildTribeStats(rows, averagePosition);
28
+ const standardDeviationOfTheMean = standardDeviation / Math.sqrt(rowsForHero.length);
29
+ const tribeStats = buildTribeStats(rowsForHero, offeredRows, averagePosition);
27
30
  const result = {
28
31
  heroCardId: ref.heroCardId,
29
- dataPoints: rows.length,
32
+ dataPoints: rowsForHero.length,
33
+ totalOffered: totalOffered,
34
+ totalPicked: totalPicked,
30
35
  averagePosition: (0, util_functions_1.round)(averagePosition),
31
36
  standardDeviation: (0, util_functions_1.round)(standardDeviation),
32
37
  standardDeviationOfTheMean: (0, util_functions_1.round)(standardDeviationOfTheMean),
@@ -39,17 +44,22 @@ const buildStatsForSingleHero = (rows) => {
39
44
  };
40
45
  return result;
41
46
  };
42
- const buildTribeStats = (rows, refAveragePosition) => {
43
- const uniqueTribes = [...new Set(rows.flatMap((r) => r.tribesExpanded))];
47
+ const buildTribeStats = (rowsForHero, offeredRows, refAveragePosition) => {
48
+ const uniqueTribes = [...new Set(rowsForHero.flatMap((r) => r.tribesExpanded))];
44
49
  return uniqueTribes.map((tribe) => {
45
- const rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));
46
- const rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));
50
+ const rowsForTribe = rowsForHero.filter((r) => r.tribesExpanded.includes(tribe));
51
+ const offeredRowsForTribe = offeredRows.filter((r) => r.tribesExpanded.includes(tribe));
52
+ const totalOffered = offeredRowsForTribe.length;
53
+ const totalPicked = offeredRowsForTribe.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;
54
+ const rowsWithoutTribe = rowsForHero.filter((r) => !r.tribesExpanded.includes(tribe));
47
55
  const averagePosition = average(rowsForTribe.map((r) => r.playerRank));
48
56
  const averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));
49
57
  const result = {
50
58
  tribe: tribe,
51
59
  dataPoints: rowsForTribe.length,
52
60
  dataPointsOnMissingTribe: rowsWithoutTribe.length,
61
+ totalOffered: totalOffered,
62
+ totalPicked: totalPicked,
53
63
  averagePosition: averagePosition,
54
64
  averagePositionWithoutTribe: averagePositionWithoutTribe,
55
65
  impactAveragePosition: averagePosition - refAveragePosition,
@@ -1 +1 @@
1
- {"version":3,"file":"hero-stats-buikder.js","sourceRoot":"","sources":["../../../src/duos/hourly/hero-stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { normalizeHeroCardId, round } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from '../../models';\r\nimport { buildCombatWinrate, buildWarbandStats } from './builders';\r\nimport { buildPlacementDistribution } from './utils';\r\n\r\nexport const buildHeroStatsForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\t// This takes about 3s, so not impactful\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => normalizeHeroCardId(row.heroCardId, allCards))(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (rows: readonly InternalBgsRow[]): BgsGlobalHeroStat => {\r\n\t// const startTime = new Date().getTime();\r\n\tconst ref = rows[0];\r\n\tconst averagePosition = average(rows.map((r) => r.playerRank));\r\n\t// const placementStartTime = new Date().getTime();\r\n\tconst placementDistribution = buildPlacementDistribution(rows);\r\n\t// const placementProcessTime = new Date().getTime() - placementStartTime;\r\n\t// const winrateStartTime = new Date().getTime();\r\n\tconst rawCombatWinrates = buildCombatWinrate(rows);\r\n\t// const winrateProcessTime = new Date().getTime() - winrateStartTime;\r\n\t// const combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \twinrate: round(info.totalWinrate / info.dataPoints),\r\n\t// }));\r\n\tconst rawWarbandStats = buildWarbandStats(rows);\r\n\t// const warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \taverageStats: round(info.totalStats / info.dataPoints),\r\n\t// }));\r\n\r\n\tconst allRanks = rows.map((r) => r.playerRank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rows.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);\r\n\t// const tribeStartTime = new Date().getTime();\r\n\tconst tribeStats = buildTribeStats(rows, averagePosition);\r\n\t// const tribeProcessTime = new Date().getTime() - tribeStartTime;\r\n\t// const anomalyStartTime = new Date().getTime();\r\n\t// const anomalyStats = buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats);\r\n\t// const anomalyProcessTime = new Date().getTime() - anomalyStartTime;\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rows.length,\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistributionRaw: placementDistribution,\r\n\t\tcombatWinrateRaw: rawCombatWinrates,\r\n\t\twarbandStatsRaw: rawWarbandStats,\r\n\t\ttribeStats: tribeStats,\r\n\t\tanomalyStats: null, //anomalyStats,\r\n\t};\r\n\t// const processTime = new Date().getTime() - startTime;\r\n\t// console.log('\\tbuilt for hero', result.heroCardId, result.dataPoints, result);\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (rows: readonly InternalBgsRow[], refAveragePosition: number): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rows.flatMap((r) => r.tribesExpanded))];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.playerRank));\r\n\t\tconst averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));\r\n\t\tconst result: BgsHeroTribeStat = {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\taveragePositionWithoutTribe: averagePositionWithoutTribe,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\timpactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,\r\n\t\t};\r\n\t\treturn result;\r\n\t});\r\n};\r\n\r\n// const buildAnomalyStats = (\r\n// \trows: readonly InternalBgsRow[],\r\n// \trefAveragePosition: number,\r\n// \trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n// \trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n// \trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n// ): readonly BgsHeroAnomalyStat[] => {\r\n// \tconst rowsWithAnomalies = rows.filter((r) => !!r.bgsAnomalies?.length);\r\n// \tconst uniqueAnomalies: readonly string[] = [...new Set(rowsWithAnomalies.flatMap((r) => r.bgsAnomalies))];\r\n// \treturn uniqueAnomalies.map((anomaly) => {\r\n// \t\tconst rowsForAnomaly = rowsWithAnomalies.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n// \t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n// \t\tconst placementDistribution = buildPlacementDistribution(rowsForAnomaly);\r\n// \t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n// \t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\twinrate: round(info.totalWinrate / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n// \t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\taverageStats: round(info.totalStats / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst result: BgsHeroAnomalyStat = {\r\n// \t\t\tanomaly: anomaly,\r\n// \t\t\tdataPoints: rowsForAnomaly.length,\r\n// \t\t\taveragePosition: round(averagePosition),\r\n// \t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n// \t\t\tplacementDistributionRaw: placementDistribution,\r\n// \t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n// \t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n// \t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n// \t\t\t\tif (!newPlacementInfo) {\r\n// \t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\trank: p.rank,\r\n// \t\t\t\t\timpact: round((newPlacementInfo?.percentage ?? 0) - p.percentage),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\tcombatWinrate: combatWinrate,\r\n// \t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n// \t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newCombatWinrate) {\r\n// \t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newCombatWinrate?.winrate ?? 0) - c.winrate),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\twarbandStats: warbandStats,\r\n// \t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n// \t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newWarbandStats) {\r\n// \t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newWarbandStats?.averageStats ?? 0) - c.averageStats),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t};\r\n// \t\treturn result;\r\n// \t});\r\n// };\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
1
+ {"version":3,"file":"hero-stats-buikder.js","sourceRoot":"","sources":["../../../src/duos/hourly/hero-stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { round } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from '../../models';\r\nimport { buildCombatWinrate, buildWarbandStats } from './builders';\r\nimport { buildPlacementDistribution } from './utils';\r\n\r\nexport const buildHeroStatsForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\t// This takes about 3s, so not impactful\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => row.heroCardId)(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data, rows));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (\r\n\trowsForHero: readonly InternalBgsRow[],\r\n\tallRows: readonly InternalBgsRow[],\r\n): BgsGlobalHeroStat => {\r\n\t// const startTime = new Date().getTime();\r\n\tconst ref = rowsForHero[0];\r\n\tconst offeredRows = allRows.filter((r) => r.heroesOptionsExpanded.includes(rowsForHero[0].heroCardId));\r\n\tconst totalOffered = offeredRows.length;\r\n\tconst totalPicked = offeredRows.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;\r\n\tconst averagePosition = average(rowsForHero.map((r) => r.playerRank));\r\n\t// const placementStartTime = new Date().getTime();\r\n\tconst placementDistribution = buildPlacementDistribution(rowsForHero);\r\n\t// const placementProcessTime = new Date().getTime() - placementStartTime;\r\n\t// const winrateStartTime = new Date().getTime();\r\n\tconst rawCombatWinrates = buildCombatWinrate(rowsForHero);\r\n\t// const winrateProcessTime = new Date().getTime() - winrateStartTime;\r\n\t// const combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \twinrate: round(info.totalWinrate / info.dataPoints),\r\n\t// }));\r\n\tconst rawWarbandStats = buildWarbandStats(rowsForHero);\r\n\t// const warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \taverageStats: round(info.totalStats / info.dataPoints),\r\n\t// }));\r\n\r\n\tconst allRanks = rowsForHero.map((r) => r.playerRank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rowsForHero.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rowsForHero.length);\r\n\t// const tribeStartTime = new Date().getTime();\r\n\tconst tribeStats = buildTribeStats(rowsForHero, offeredRows, averagePosition);\r\n\t// const tribeProcessTime = new Date().getTime() - tribeStartTime;\r\n\t// const anomalyStartTime = new Date().getTime();\r\n\t// const anomalyStats = buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats);\r\n\t// const anomalyProcessTime = new Date().getTime() - anomalyStartTime;\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rowsForHero.length,\r\n\t\ttotalOffered: totalOffered,\r\n\t\ttotalPicked: totalPicked,\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistributionRaw: placementDistribution,\r\n\t\tcombatWinrateRaw: rawCombatWinrates,\r\n\t\twarbandStatsRaw: rawWarbandStats,\r\n\t\ttribeStats: tribeStats,\r\n\t\tanomalyStats: null, //anomalyStats,\r\n\t};\r\n\t// const processTime = new Date().getTime() - startTime;\r\n\t// console.log('\\tbuilt for hero', result.heroCardId, result.dataPoints, result);\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (\r\n\trowsForHero: readonly InternalBgsRow[],\r\n\tofferedRows: readonly InternalBgsRow[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rowsForHero.flatMap((r) => r.tribesExpanded))];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rowsForHero.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst offeredRowsForTribe = offeredRows.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst totalOffered = offeredRowsForTribe.length;\r\n\t\tconst totalPicked = offeredRowsForTribe.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;\r\n\t\tconst rowsWithoutTribe = rowsForHero.filter((r) => !r.tribesExpanded.includes(tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.playerRank));\r\n\t\tconst averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));\r\n\t\tconst result: BgsHeroTribeStat = {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\ttotalOffered: totalOffered,\r\n\t\t\ttotalPicked: totalPicked,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\taveragePositionWithoutTribe: averagePositionWithoutTribe,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\timpactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,\r\n\t\t};\r\n\t\treturn result;\r\n\t});\r\n};\r\n\r\n// const buildAnomalyStats = (\r\n// \trows: readonly InternalBgsRow[],\r\n// \trefAveragePosition: number,\r\n// \trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n// \trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n// \trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n// ): readonly BgsHeroAnomalyStat[] => {\r\n// \tconst rowsWithAnomalies = rows.filter((r) => !!r.bgsAnomalies?.length);\r\n// \tconst uniqueAnomalies: readonly string[] = [...new Set(rowsWithAnomalies.flatMap((r) => r.bgsAnomalies))];\r\n// \treturn uniqueAnomalies.map((anomaly) => {\r\n// \t\tconst rowsForAnomaly = rowsWithAnomalies.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n// \t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n// \t\tconst placementDistribution = buildPlacementDistribution(rowsForAnomaly);\r\n// \t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n// \t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\twinrate: round(info.totalWinrate / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n// \t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\taverageStats: round(info.totalStats / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst result: BgsHeroAnomalyStat = {\r\n// \t\t\tanomaly: anomaly,\r\n// \t\t\tdataPoints: rowsForAnomaly.length,\r\n// \t\t\taveragePosition: round(averagePosition),\r\n// \t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n// \t\t\tplacementDistributionRaw: placementDistribution,\r\n// \t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n// \t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n// \t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n// \t\t\t\tif (!newPlacementInfo) {\r\n// \t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\trank: p.rank,\r\n// \t\t\t\t\timpact: round((newPlacementInfo?.percentage ?? 0) - p.percentage),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\tcombatWinrate: combatWinrate,\r\n// \t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n// \t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newCombatWinrate) {\r\n// \t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newCombatWinrate?.winrate ?? 0) - c.winrate),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\twarbandStats: warbandStats,\r\n// \t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n// \t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newWarbandStats) {\r\n// \t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newWarbandStats?.averageStats ?? 0) - c.averageStats),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t};\r\n// \t\treturn result;\r\n// \t});\r\n// };\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
@@ -149,10 +149,12 @@ const processRows = async (rows, multipartUpload, allCards) => {
149
149
  row.heroCardId !== "BG22_HERO_007t")
150
150
  .filter((row) => { var _a; return !!row.playerRank && !!((_a = row.tribes) === null || _a === void 0 ? void 0 : _a.length); })
151
151
  .map((row) => {
152
+ var _a, _b;
152
153
  const result = {
153
154
  ...row,
154
155
  heroCardId: (0, util_functions_1.normalizeHeroCardId)(row.heroCardId, allCards),
155
156
  tribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),
157
+ heroesOptionsExpanded: (_b = (_a = row.heroesOptions) === null || _a === void 0 ? void 0 : _a.split(',').map((hero) => (0, util_functions_1.normalizeHeroCardId)(hero, allCards))) !== null && _b !== void 0 ? _b : [],
156
158
  };
157
159
  delete result.reviewId;
158
160
  delete result.tribes;
@@ -1 +1 @@
1
- {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/duos/hourly/rows.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3Multipart } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { S3 as S3AWS } from 'aws-sdk';\r\nimport SecretsManager, { GetSecretValueRequest, GetSecretValueResponse } from 'aws-sdk/clients/secretsmanager';\r\nimport { Connection, createPool } from 'mysql';\r\nimport { Readable } from 'stream';\r\nimport { normalizeHeroCardId } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { STATS_BUCKET, WORKING_ROWS_FILE, s3 } from './_build-battlegrounds-hero-stats';\r\n\r\nexport const readRowsFromS3 = async (startDate: string): Promise<readonly InternalBgsRow[]> => {\r\n\treturn new Promise<readonly InternalBgsRow[]>((resolve, reject) => {\r\n\t\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate)}`;\r\n\t\tconsole.debug('reading rows from s3', workingRowsFile);\r\n\t\tlet parseErrors = 0;\r\n\t\tlet totalParsed = 0;\r\n\t\tconst stream: Readable = s3.readStream(STATS_BUCKET, workingRowsFile);\r\n\t\tconst result: InternalBgsRow[] = [];\r\n\t\tlet previousString = '';\r\n\t\tlet emptyRowsInARow = 0;\r\n\t\tstream\r\n\t\t\t.on('data', (chunk) => {\r\n\t\t\t\tconst str = Buffer.from(chunk).toString('utf-8');\r\n\t\t\t\tconst newStr = previousString + str;\r\n\t\t\t\tconst split = newStr.split('\\n');\r\n\t\t\t\tconst rows: readonly InternalBgsRow[] = split.slice(0, split.length - 1).map((row) => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst result: InternalBgsRow = JSON.parse(row);\r\n\t\t\t\t\t\ttotalParsed++;\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t} catch (e) {\r\n\t\t\t\t\t\t// logger.warn('could not parse row', row);\r\n\t\t\t\t\t\tparseErrors++;\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\tpreviousString = split[split.length - 1];\r\n\t\t\t\tresult.push(...rows);\r\n\r\n\t\t\t\t// Do this to avoid errors in case the chunks are small compared to the row sizes\r\n\t\t\t\tif (result.length === 0 && rows.length === 0) {\r\n\t\t\t\t\temptyRowsInARow++;\r\n\t\t\t\t} else {\r\n\t\t\t\t\temptyRowsInARow = 0;\r\n\t\t\t\t}\r\n\t\t\t\tif (emptyRowsInARow > 50) {\r\n\t\t\t\t\tconsole.error(newStr);\r\n\t\t\t\t\tconsole.error(split);\r\n\t\t\t\t\tthrow new Error('Could not parse any row');\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', () => {\r\n\t\t\t\tconst finalResult = result.filter((row) => !!row);\r\n\t\t\t\tconsole.log('stream end', result.length, finalResult.length);\r\n\t\t\t\tconsole.log('parsing errors', parseErrors, 'and successes', totalParsed);\r\n\t\t\t\tresolve(finalResult);\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nexport const saveRowsOnS3 = async (startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\tconsole.log('will export rows to S3', startDate, endDate);\r\n\tconst secretRequest: GetSecretValueRequest = {\r\n\t\tSecretId: 'rds-connection',\r\n\t};\r\n\tconst secret: SecretInfo = await getSecret(secretRequest);\r\n\tconst pool = createPool({\r\n\t\tconnectionLimit: 1,\r\n\t\thost: secret.hostReadOnly,\r\n\t\tuser: secret.username,\r\n\t\tpassword: secret.password,\r\n\t\tdatabase: 'replay_summary',\r\n\t\tport: secret.port,\r\n\t});\r\n\r\n\ttry {\r\n\t\tawait performRowProcessIngPool(pool, startDate, endDate, allCards);\r\n\t} finally {\r\n\t\tpool.end((err) => {\r\n\t\t\tconsole.log('ending pool', err);\r\n\t\t});\r\n\t}\r\n};\r\n\r\nconst performRowProcessIngPool = async (pool: any, startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tpool.getConnection(async (err, connection) => {\r\n\t\t\tif (err) {\r\n\t\t\t\tconsole.log('error with connection', err);\r\n\t\t\t\tthrow new Error('Could not connect to DB');\r\n\t\t\t} else {\r\n\t\t\t\tawait performRowsProcessing(connection, startDate, endDate, allCards);\r\n\t\t\t\tconnection.release();\r\n\t\t\t}\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n};\r\n\r\nconst performRowsProcessing = async (\r\n\tconnection: Connection,\r\n\tstartDate: Date,\r\n\tendDate: Date,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst multipartUpload = new S3Multipart(new S3AWS());\r\n\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate.toISOString())}`;\r\n\tawait multipartUpload.initMultipart('static.zerotoheroes.com', workingRowsFile, 'application/json');\r\n\tconsole.log('multipart upload init', workingRowsFile);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tconst queryStr = `\r\n\t\t\tSELECT * FROM bgs_run_stats_duo\r\n\t\t\tWHERE creationDate >= ?\r\n\t\t\tAND creationDate < ?\r\n\t\t`;\r\n\t\tconsole.log('running query', queryStr);\r\n\t\tconst query = connection.query(queryStr, [startDate, endDate]);\r\n\r\n\t\tlet rowsToProcess = [];\r\n\t\tlet rowCount = 0;\r\n\t\tquery\r\n\t\t\t.on('error', (err) => {\r\n\t\t\t\tconsole.error('error while fetching rows', err);\r\n\t\t\t})\r\n\t\t\t.on('fields', (fields) => {\r\n\t\t\t\tconsole.log('fields', fields);\r\n\t\t\t})\r\n\t\t\t.on('result', async (row) => {\r\n\t\t\t\trowsToProcess.push(row);\r\n\t\t\t\tif (rowsToProcess.length > 20000 && !multipartUpload.processing) {\r\n\t\t\t\t\tconnection.pause();\r\n\t\t\t\t\t// console.log('before upload', rowsToProcess.length);\r\n\t\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\t\trowsToProcess = [];\r\n\t\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\t\trowCount += uploaded;\r\n\t\t\t\t\tconsole.log('processed rows', uploaded, '/', toUpload.length, rowCount);\r\n\t\t\t\t\tconnection.resume();\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', async () => {\r\n\t\t\t\tconsole.log('end');\r\n\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\trowsToProcess = [];\r\n\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\trowCount += uploaded;\r\n\t\t\t\tconsole.log('processed rows', uploaded, rowCount);\r\n\t\t\t\t// connection.resume();\r\n\t\t\t\tawait multipartUpload.completeMultipart();\r\n\t\t\t\tresolve();\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nconst processRows = async (\r\n\trows: readonly InternalBgsRow[],\r\n\tmultipartUpload: S3Multipart,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst validRows = rows\r\n\t\t// .filter((row) => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(\r\n\t\t\t(row) =>\r\n\t\t\t\trow.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedToken &&\r\n\t\t\t\trow.heroCardId !== CardIds.QueenAzshara_NagaQueenAzsharaToken,\r\n\t\t)\r\n\t\t.filter((row) => !!row.playerRank && !!row.tribes?.length)\r\n\t\t.map((row) => {\r\n\t\t\tconst result: InternalBgsRow = {\r\n\t\t\t\t...row,\r\n\t\t\t\theroCardId: normalizeHeroCardId(row.heroCardId, allCards),\r\n\t\t\t\ttribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),\r\n\t\t\t};\r\n\t\t\tdelete (result as any).reviewId;\r\n\t\t\tdelete (result as any).tribes;\r\n\t\t\treturn result;\r\n\t\t});\r\n\tif (validRows.length > 0) {\r\n\t\t// console.log('\\t', 'uploading', validRows.length, 'rows');\r\n\t\tawait multipartUpload.uploadPart(validRows.map((r) => JSON.stringify(r)).join('\\n'));\r\n\t}\r\n\treturn validRows.length;\r\n};\r\n\r\nconst getSecret = (secretRequest: GetSecretValueRequest) => {\r\n\tconst secretsManager = new SecretsManager({ region: 'us-west-2' });\r\n\treturn new Promise<SecretInfo>((resolve) => {\r\n\t\tsecretsManager.getSecretValue(secretRequest, (err, data: GetSecretValueResponse) => {\r\n\t\t\tconst secretInfo: SecretInfo = JSON.parse(data.SecretString);\r\n\t\t\tresolve(secretInfo);\r\n\t\t});\r\n\t});\r\n};\r\n\r\ninterface SecretInfo {\r\n\treadonly username: string;\r\n\treadonly password: string;\r\n\treadonly host: string;\r\n\treadonly hostReadOnly: string;\r\n\treadonly port: number;\r\n\treadonly dbClusterIdentifier: string;\r\n}\r\n"]}
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/duos/hourly/rows.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3Multipart } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { S3 as S3AWS } from 'aws-sdk';\r\nimport SecretsManager, { GetSecretValueRequest, GetSecretValueResponse } from 'aws-sdk/clients/secretsmanager';\r\nimport { Connection, createPool } from 'mysql';\r\nimport { Readable } from 'stream';\r\nimport { normalizeHeroCardId } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { STATS_BUCKET, WORKING_ROWS_FILE, s3 } from './_build-battlegrounds-hero-stats';\r\n\r\nexport const readRowsFromS3 = async (startDate: string): Promise<readonly InternalBgsRow[]> => {\r\n\treturn new Promise<readonly InternalBgsRow[]>((resolve, reject) => {\r\n\t\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate)}`;\r\n\t\tconsole.debug('reading rows from s3', workingRowsFile);\r\n\t\tlet parseErrors = 0;\r\n\t\tlet totalParsed = 0;\r\n\t\tconst stream: Readable = s3.readStream(STATS_BUCKET, workingRowsFile);\r\n\t\tconst result: InternalBgsRow[] = [];\r\n\t\tlet previousString = '';\r\n\t\tlet emptyRowsInARow = 0;\r\n\t\tstream\r\n\t\t\t.on('data', (chunk) => {\r\n\t\t\t\tconst str = Buffer.from(chunk).toString('utf-8');\r\n\t\t\t\tconst newStr = previousString + str;\r\n\t\t\t\tconst split = newStr.split('\\n');\r\n\t\t\t\tconst rows: readonly InternalBgsRow[] = split.slice(0, split.length - 1).map((row) => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst result: InternalBgsRow = JSON.parse(row);\r\n\t\t\t\t\t\ttotalParsed++;\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t} catch (e) {\r\n\t\t\t\t\t\t// logger.warn('could not parse row', row);\r\n\t\t\t\t\t\tparseErrors++;\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\tpreviousString = split[split.length - 1];\r\n\t\t\t\tresult.push(...rows);\r\n\r\n\t\t\t\t// Do this to avoid errors in case the chunks are small compared to the row sizes\r\n\t\t\t\tif (result.length === 0 && rows.length === 0) {\r\n\t\t\t\t\temptyRowsInARow++;\r\n\t\t\t\t} else {\r\n\t\t\t\t\temptyRowsInARow = 0;\r\n\t\t\t\t}\r\n\t\t\t\tif (emptyRowsInARow > 50) {\r\n\t\t\t\t\tconsole.error(newStr);\r\n\t\t\t\t\tconsole.error(split);\r\n\t\t\t\t\tthrow new Error('Could not parse any row');\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', () => {\r\n\t\t\t\tconst finalResult = result.filter((row) => !!row);\r\n\t\t\t\tconsole.log('stream end', result.length, finalResult.length);\r\n\t\t\t\tconsole.log('parsing errors', parseErrors, 'and successes', totalParsed);\r\n\t\t\t\tresolve(finalResult);\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nexport const saveRowsOnS3 = async (startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\tconsole.log('will export rows to S3', startDate, endDate);\r\n\tconst secretRequest: GetSecretValueRequest = {\r\n\t\tSecretId: 'rds-connection',\r\n\t};\r\n\tconst secret: SecretInfo = await getSecret(secretRequest);\r\n\tconst pool = createPool({\r\n\t\tconnectionLimit: 1,\r\n\t\thost: secret.hostReadOnly,\r\n\t\tuser: secret.username,\r\n\t\tpassword: secret.password,\r\n\t\tdatabase: 'replay_summary',\r\n\t\tport: secret.port,\r\n\t});\r\n\r\n\ttry {\r\n\t\tawait performRowProcessIngPool(pool, startDate, endDate, allCards);\r\n\t} finally {\r\n\t\tpool.end((err) => {\r\n\t\t\tconsole.log('ending pool', err);\r\n\t\t});\r\n\t}\r\n};\r\n\r\nconst performRowProcessIngPool = async (pool: any, startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tpool.getConnection(async (err, connection) => {\r\n\t\t\tif (err) {\r\n\t\t\t\tconsole.log('error with connection', err);\r\n\t\t\t\tthrow new Error('Could not connect to DB');\r\n\t\t\t} else {\r\n\t\t\t\tawait performRowsProcessing(connection, startDate, endDate, allCards);\r\n\t\t\t\tconnection.release();\r\n\t\t\t}\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n};\r\n\r\nconst performRowsProcessing = async (\r\n\tconnection: Connection,\r\n\tstartDate: Date,\r\n\tendDate: Date,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst multipartUpload = new S3Multipart(new S3AWS());\r\n\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate.toISOString())}`;\r\n\tawait multipartUpload.initMultipart('static.zerotoheroes.com', workingRowsFile, 'application/json');\r\n\tconsole.log('multipart upload init', workingRowsFile);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tconst queryStr = `\r\n\t\t\tSELECT * FROM bgs_run_stats_duo\r\n\t\t\tWHERE creationDate >= ?\r\n\t\t\tAND creationDate < ?\r\n\t\t`;\r\n\t\tconsole.log('running query', queryStr);\r\n\t\tconst query = connection.query(queryStr, [startDate, endDate]);\r\n\r\n\t\tlet rowsToProcess = [];\r\n\t\tlet rowCount = 0;\r\n\t\tquery\r\n\t\t\t.on('error', (err) => {\r\n\t\t\t\tconsole.error('error while fetching rows', err);\r\n\t\t\t})\r\n\t\t\t.on('fields', (fields) => {\r\n\t\t\t\tconsole.log('fields', fields);\r\n\t\t\t})\r\n\t\t\t.on('result', async (row) => {\r\n\t\t\t\trowsToProcess.push(row);\r\n\t\t\t\tif (rowsToProcess.length > 20000 && !multipartUpload.processing) {\r\n\t\t\t\t\tconnection.pause();\r\n\t\t\t\t\t// console.log('before upload', rowsToProcess.length);\r\n\t\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\t\trowsToProcess = [];\r\n\t\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\t\trowCount += uploaded;\r\n\t\t\t\t\tconsole.log('processed rows', uploaded, '/', toUpload.length, rowCount);\r\n\t\t\t\t\tconnection.resume();\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', async () => {\r\n\t\t\t\tconsole.log('end');\r\n\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\trowsToProcess = [];\r\n\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\trowCount += uploaded;\r\n\t\t\t\tconsole.log('processed rows', uploaded, rowCount);\r\n\t\t\t\t// connection.resume();\r\n\t\t\t\tawait multipartUpload.completeMultipart();\r\n\t\t\t\tresolve();\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nconst processRows = async (\r\n\trows: readonly InternalBgsRow[],\r\n\tmultipartUpload: S3Multipart,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst validRows = rows\r\n\t\t// .filter((row) => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(\r\n\t\t\t(row) =>\r\n\t\t\t\trow.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedToken &&\r\n\t\t\t\trow.heroCardId !== CardIds.QueenAzshara_NagaQueenAzsharaToken,\r\n\t\t)\r\n\t\t.filter((row) => !!row.playerRank && !!row.tribes?.length)\r\n\t\t.map((row) => {\r\n\t\t\tconst result: InternalBgsRow = {\r\n\t\t\t\t...row,\r\n\t\t\t\theroCardId: normalizeHeroCardId(row.heroCardId, allCards),\r\n\t\t\t\ttribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),\r\n\t\t\t\theroesOptionsExpanded:\r\n\t\t\t\t\trow.heroesOptions?.split(',').map((hero) => normalizeHeroCardId(hero, allCards)) ?? [],\r\n\t\t\t};\r\n\t\t\tdelete (result as any).reviewId;\r\n\t\t\tdelete (result as any).tribes;\r\n\t\t\treturn result;\r\n\t\t});\r\n\tif (validRows.length > 0) {\r\n\t\t// console.log('\\t', 'uploading', validRows.length, 'rows');\r\n\t\tawait multipartUpload.uploadPart(validRows.map((r) => JSON.stringify(r)).join('\\n'));\r\n\t}\r\n\treturn validRows.length;\r\n};\r\n\r\nconst getSecret = (secretRequest: GetSecretValueRequest) => {\r\n\tconst secretsManager = new SecretsManager({ region: 'us-west-2' });\r\n\treturn new Promise<SecretInfo>((resolve) => {\r\n\t\tsecretsManager.getSecretValue(secretRequest, (err, data: GetSecretValueResponse) => {\r\n\t\t\tconst secretInfo: SecretInfo = JSON.parse(data.SecretString);\r\n\t\t\tresolve(secretInfo);\r\n\t\t});\r\n\t});\r\n};\r\n\r\ninterface SecretInfo {\r\n\treadonly username: string;\r\n\treadonly password: string;\r\n\treadonly host: string;\r\n\treadonly hostReadOnly: string;\r\n\treadonly port: number;\r\n\treadonly dbClusterIdentifier: string;\r\n}\r\n"]}
@@ -20,6 +20,8 @@ export interface InternalBgsRow {
20
20
  readonly bgsAnomalies: string;
21
21
  readonly bgsTrinkets: string;
22
22
  readonly bgsTrinketsOptions: string;
23
+ readonly heroesOptions: string;
24
+ readonly heroesOptionsExpanded: readonly string[];
23
25
  }
24
26
  import { MmrPercentile, WithMmrAndTimePeriod } from './models';
25
27
  export interface InternalBgsTrinketStats {
@@ -1 +1 @@
1
- {"version":3,"file":"internal-model.js","sourceRoot":"","sources":["../src/internal-model.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\nexport interface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly reviewId: string;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\treadonly heroCardId: string;\r\n\treadonly playerRank: number;\r\n\t/** @deprecated */\r\n\treadonly tribes: string;\r\n\treadonly tribesExpanded: readonly Race[];\r\n\t/** @deprecated */\r\n\treadonly combatWinrate: string;\r\n\t// readonly combatWinrateExpanded: readonly { turn: number; winrate: number }[];\r\n\t/** @deprecated */\r\n\treadonly warbandStats: string;\r\n\t// readonly warbandStatsExpanded: readonly { turn: number; totalStats: number }[];\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly quests: boolean;\r\n\treadonly bgsHeroQuests: string;\r\n\treadonly bgsQuestsCompletedTimings: string;\r\n\treadonly bgsQuestsDifficulties: string;\r\n\treadonly bgsHeroQuestRewards: string;\r\n\treadonly bgsAnomalies: string;\r\n\treadonly bgsTrinkets: string;\r\n\treadonly bgsTrinketsOptions: string;\r\n}\r\n\r\nimport { MmrPercentile, WithMmrAndTimePeriod } from './models';\r\n\r\nexport interface InternalBgsTrinketStats {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly trinketStats: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[];\r\n}\r\n\r\nexport interface InternalBgsGlobalTrinketStat {\r\n\treadonly trinketCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly totalOffered: number;\r\n\treadonly averagePlacement: number;\r\n\treadonly heroStats: readonly InternalBgsTrinketHeroStat[];\r\n}\r\n\r\nexport interface InternalBgsTrinketHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePlacement: number;\r\n}\r\n"]}
1
+ {"version":3,"file":"internal-model.js","sourceRoot":"","sources":["../src/internal-model.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\n// bgs_run_stats and bgs_run_stats_duo\r\nexport interface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly reviewId: string;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\t// Normalized once it goes out of the \"rows.ts\" process\r\n\treadonly heroCardId: string;\r\n\treadonly playerRank: number;\r\n\t/** @deprecated */\r\n\treadonly tribes: string;\r\n\treadonly tribesExpanded: readonly Race[];\r\n\t/** @deprecated */\r\n\treadonly combatWinrate: string;\r\n\t// readonly combatWinrateExpanded: readonly { turn: number; winrate: number }[];\r\n\t/** @deprecated */\r\n\treadonly warbandStats: string;\r\n\t// readonly warbandStatsExpanded: readonly { turn: number; totalStats: number }[];\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly quests: boolean;\r\n\treadonly bgsHeroQuests: string;\r\n\treadonly bgsQuestsCompletedTimings: string;\r\n\treadonly bgsQuestsDifficulties: string;\r\n\treadonly bgsHeroQuestRewards: string;\r\n\treadonly bgsAnomalies: string;\r\n\treadonly bgsTrinkets: string;\r\n\treadonly bgsTrinketsOptions: string;\r\n\t/** @deprecated */\r\n\treadonly heroesOptions: string;\r\n\treadonly heroesOptionsExpanded: readonly string[];\r\n}\r\n\r\nimport { MmrPercentile, WithMmrAndTimePeriod } from './models';\r\n\r\nexport interface InternalBgsTrinketStats {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly trinketStats: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[];\r\n}\r\n\r\nexport interface InternalBgsGlobalTrinketStat {\r\n\treadonly trinketCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly totalOffered: number;\r\n\treadonly averagePlacement: number;\r\n\treadonly heroStats: readonly InternalBgsTrinketHeroStat[];\r\n}\r\n\r\nexport interface InternalBgsTrinketHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePlacement: number;\r\n}\r\n"]}
package/dist/models.d.ts CHANGED
@@ -8,6 +8,8 @@ export interface BgsHeroStatsV2 {
8
8
  export interface BgsGlobalHeroStat {
9
9
  readonly heroCardId: string;
10
10
  readonly dataPoints: number;
11
+ readonly totalOffered: number;
12
+ readonly totalPicked: number;
11
13
  readonly averagePosition: number;
12
14
  readonly standardDeviation: number;
13
15
  readonly standardDeviationOfTheMean: number;
@@ -45,6 +47,8 @@ export interface BgsHeroTribeStat {
45
47
  readonly tribe: Race;
46
48
  readonly dataPoints: number;
47
49
  readonly dataPointsOnMissingTribe: number;
50
+ readonly totalOffered: number;
51
+ readonly totalPicked: number;
48
52
  readonly averagePosition: number;
49
53
  readonly averagePositionWithoutTribe: number;
50
54
  readonly impactAveragePosition: number;
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\nexport interface BgsHeroStatsV2 {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly WithMmrAndTimePeriod<BgsGlobalHeroStat>[];\r\n}\r\n\r\nexport interface BgsGlobalHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly standardDeviation: number;\r\n\treadonly standardDeviationOfTheMean: number;\r\n\treadonly conservativePositionEstimate: number;\r\n\treadonly placementDistribution?: readonly { rank: number; percentage: number }[];\r\n\treadonly placementDistributionRaw?: readonly { rank: number; totalMatches: number }[];\r\n\treadonly combatWinrate?: readonly { turn: number; winrate: number }[];\r\n\treadonly combatWinrateRaw?: readonly { turn: number; dataPoints: number; totalWinrate: number }[];\r\n\treadonly warbandStats?: readonly { turn: number; averageStats: number }[];\r\n\treadonly warbandStatsRaw?: readonly { turn: number; dataPoints: number; totalStats: number }[];\r\n\treadonly tribeStats: readonly BgsHeroTribeStat[];\r\n\treadonly anomalyStats: readonly BgsHeroAnomalyStat[];\r\n}\r\n\r\nexport interface BgsHeroTribeStat {\r\n\treadonly tribe: Race;\r\n\treadonly dataPoints: number;\r\n\treadonly dataPointsOnMissingTribe: number;\r\n\treadonly averagePosition: number;\r\n\treadonly averagePositionWithoutTribe: number;\r\n\t// Impacts are only meant to be used in the final output\r\n\treadonly impactAveragePosition: number;\r\n\treadonly impactAveragePositionVsMissingTribe: number;\r\n\t// readonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\t// readonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\t// readonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\t// readonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\t// readonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\t// readonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n\r\nexport interface BgsHeroAnomalyStat {\r\n\treadonly anomaly: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly impactAveragePosition: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly placementDistributionRaw: readonly { rank: number; totalMatches: number }[];\r\n\treadonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n\r\nexport type WithMmrAndTimePeriod<T> = T & {\r\n\treadonly mmrPercentile: MmrPercentileFilter;\r\n\treadonly timePeriod: TimePeriod;\r\n};\r\n\r\nexport interface MmrPercentile {\r\n\treadonly mmr: number;\r\n\treadonly percentile: MmrPercentileFilter;\r\n}\r\n\r\nexport type TimePeriod = 'all-time' | 'past-three' | 'past-seven' | 'last-patch';\r\nexport type MmrPercentileFilter = 100 | 50 | 25 | 10 | 1;\r\n"]}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"","sourcesContent":["import { Race } from '@firestone-hs/reference-data';\r\n\r\nexport interface BgsHeroStatsV2 {\r\n\treadonly lastUpdateDate: Date;\r\n\treadonly mmrPercentiles: readonly MmrPercentile[];\r\n\treadonly dataPoints: number;\r\n\treadonly heroStats: readonly WithMmrAndTimePeriod<BgsGlobalHeroStat>[];\r\n}\r\n\r\nexport interface BgsGlobalHeroStat {\r\n\treadonly heroCardId: string;\r\n\treadonly dataPoints: number;\r\n\treadonly totalOffered: number;\r\n\treadonly totalPicked: number;\r\n\treadonly averagePosition: number;\r\n\treadonly standardDeviation: number;\r\n\treadonly standardDeviationOfTheMean: number;\r\n\treadonly conservativePositionEstimate: number;\r\n\treadonly placementDistribution?: readonly { rank: number; percentage: number }[];\r\n\treadonly placementDistributionRaw?: readonly { rank: number; totalMatches: number }[];\r\n\treadonly combatWinrate?: readonly { turn: number; winrate: number }[];\r\n\treadonly combatWinrateRaw?: readonly { turn: number; dataPoints: number; totalWinrate: number }[];\r\n\treadonly warbandStats?: readonly { turn: number; averageStats: number }[];\r\n\treadonly warbandStatsRaw?: readonly { turn: number; dataPoints: number; totalStats: number }[];\r\n\treadonly tribeStats: readonly BgsHeroTribeStat[];\r\n\treadonly anomalyStats: readonly BgsHeroAnomalyStat[];\r\n}\r\n\r\nexport interface BgsHeroTribeStat {\r\n\treadonly tribe: Race;\r\n\treadonly dataPoints: number;\r\n\treadonly dataPointsOnMissingTribe: number;\r\n\treadonly totalOffered: number;\r\n\treadonly totalPicked: number;\r\n\treadonly averagePosition: number;\r\n\treadonly averagePositionWithoutTribe: number;\r\n\t// Impacts are only meant to be used in the final output\r\n\treadonly impactAveragePosition: number;\r\n\treadonly impactAveragePositionVsMissingTribe: number;\r\n\t// readonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\t// readonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\t// readonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\t// readonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\t// readonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\t// readonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n\r\nexport interface BgsHeroAnomalyStat {\r\n\treadonly anomaly: string;\r\n\treadonly dataPoints: number;\r\n\treadonly averagePosition: number;\r\n\treadonly impactAveragePosition: number;\r\n\treadonly placementDistribution: readonly { rank: number; percentage: number }[];\r\n\treadonly placementDistributionRaw: readonly { rank: number; totalMatches: number }[];\r\n\treadonly impactPlacementDistribution: readonly { rank: number; impact: number }[];\r\n\treadonly combatWinrate: readonly { turn: number; winrate: number }[];\r\n\treadonly impactCombatWinrate: readonly { turn: number; impact: number }[];\r\n\treadonly warbandStats: readonly { turn: number; averageStats: number }[];\r\n\treadonly impactWarbandStats: readonly { turn: number; impact: number }[];\r\n}\r\n\r\nexport type WithMmrAndTimePeriod<T> = T & {\r\n\treadonly mmrPercentile: MmrPercentileFilter;\r\n\treadonly timePeriod: TimePeriod;\r\n};\r\n\r\nexport interface MmrPercentile {\r\n\treadonly mmr: number;\r\n\treadonly percentile: MmrPercentileFilter;\r\n}\r\n\r\nexport type TimePeriod = 'all-time' | 'past-three' | 'past-seven' | 'last-patch';\r\nexport type MmrPercentileFilter = 100 | 50 | 25 | 10 | 1;\r\n"]}
@@ -30,6 +30,8 @@ const mergeStatsForSingleHero = (stats, allCards) => {
30
30
  const result = {
31
31
  heroCardId: ref.heroCardId,
32
32
  dataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),
33
+ totalOffered: stats.map((stat) => { var _a; return (_a = stat.totalOffered) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
34
+ totalPicked: stats.map((stat) => { var _a; return (_a = stat.totalPicked) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
33
35
  averagePosition: (0, util_functions_1.round)(averagePosition),
34
36
  standardDeviation: (0, util_functions_1.round)(standardDeviation),
35
37
  standardDeviationOfTheMean: (0, util_functions_1.round)(standardDeviationOfTheMean),
@@ -59,11 +61,13 @@ const mergeTribeStats = (stats, refAveragePosition) => {
59
61
  dataPointsOnMissingTribe: tribeStats
60
62
  .map((stat) => stat.dataPointsOnMissingTribe)
61
63
  .reduce((a, b) => a + b, 0),
64
+ totalOffered: tribeStats.map((stat) => { var _a; return (_a = stat.totalOffered) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
65
+ totalPicked: tribeStats.map((stat) => { var _a; return (_a = stat.totalPicked) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0),
62
66
  averagePosition: (0, util_functions_1.round)(averagePosition),
63
67
  averagePositionWithoutTribe: (0, util_functions_1.round)(averagePositionWithoutTribe),
64
68
  refAveragePosition: (0, util_functions_1.round)(refAveragePosition),
65
69
  impactAveragePosition: (0, util_functions_1.round)(averagePosition - refAveragePosition),
66
- impactAveragePositionVsMissingTribe: (0, util_functions_1.round)(averagePositionWithoutTribe - refAveragePosition),
70
+ impactAveragePositionVsMissingTribe: (0, util_functions_1.round)(averagePosition - averagePositionWithoutTribe),
67
71
  };
68
72
  });
69
73
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { round } from '../../../common/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, BgsHeroTribeStat, MmrPercentileFilter } from '../../../models';\r\n\r\nexport const mergeStats = (\r\n\thourlyData: readonly BgsHeroStatsV2[],\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst allStats: readonly BgsGlobalHeroStat[] = hourlyData\r\n\t\t.flatMap((data) => data.heroStats)\r\n\t\t.filter((stat) => stat.mmrPercentile === mmrPercentile)\r\n\t\t.filter((stat) => stat.heroCardId !== CardIds.BaconphheroHeroic);\r\n\t// console.debug('allStats', mmrPercentile, allStats.length, '/', hourlyData.length);\r\n\tconst groupedByHero: {\r\n\t\t[heroCardId: string]: readonly BgsGlobalHeroStat[];\r\n\t} = groupByFunction((stat: BgsGlobalHeroStat) => stat.heroCardId)(allStats);\r\n\tconst result: readonly BgsGlobalHeroStat[] = Object.values(groupedByHero).map((stats) =>\r\n\t\tmergeStatsForSingleHero(stats, allCards),\r\n\t);\r\n\tconst maxDataPoints = Math.max(...result.map((r) => r.dataPoints));\r\n\tconst filtered = result.filter((r) => r.dataPoints > maxDataPoints / 50);\r\n\treturn filtered;\r\n};\r\n\r\nconst mergeStatsForSingleHero = (stats: readonly BgsGlobalHeroStat[], allCards: AllCardsService): BgsGlobalHeroStat => {\r\n\tconst ref = stats[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_010';\r\n\r\n\tconst totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst averagePosition = totalWeightedAverage / totalDataPoints;\r\n\r\n\tconst totalWeightedVariance = stats\r\n\t\t.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)\r\n\t\t.reduce((a, b) => a + b, 0);\r\n\tconst overallVariance = totalWeightedVariance / totalDataPoints;\r\n\tconst standardDeviation = Math.sqrt(overallVariance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);\r\n\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistribution: mergePlacementDistributions(stats),\r\n\t\twarbandStats: mergeWarbandStats(stats),\r\n\t\tcombatWinrate: mergeCombatWinrate(stats, debug),\r\n\t\ttribeStats: mergeTribeStats(stats, averagePosition).filter(\r\n\t\t\t(s) => s.dataPointsOnMissingTribe > s.dataPoints / 20,\r\n\t\t),\r\n\t\tanomalyStats: [], // mergeAnomalyStats(stats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst mergeTribeStats = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst allTribeStats = stats.flatMap((stat) => stat.tribeStats);\r\n\tconst uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));\r\n\tconst result: BgsHeroTribeStat[] = [...uniqueTribes].map((tribe) => {\r\n\t\tconst tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);\r\n\t\tconst averagePosition =\r\n\t\t\ttribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst averagePositionWithoutTribe =\r\n\t\t\ttribeStats\r\n\t\t\t\t.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePosition =\r\n\t\t// \ttribeStats.map((stat) => stat.impactAveragePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t// \ttribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePositionVsMissingTribe =\r\n\t\t// \ttribeStats\r\n\t\t// \t\t.map((stat) => stat.impactAveragePositionVsMissingTribe * stat.dataPoints)\r\n\t\t// \t\t.reduce((a, b) => a + b, 0) / tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\t\tdataPointsOnMissingTribe: tribeStats\r\n\t\t\t\t.map((stat) => stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0),\r\n\t\t\taveragePosition: round(averagePosition),\r\n\t\t\taveragePositionWithoutTribe: round(averagePositionWithoutTribe),\r\n\t\t\trefAveragePosition: round(refAveragePosition),\r\n\t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n\t\t\t// impactAveragePositionDebug: round(impactAveragePosition),\r\n\t\t\timpactAveragePositionVsMissingTribe: round(averagePositionWithoutTribe - refAveragePosition),\r\n\t\t};\r\n\t});\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributions = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; percentage: number }[] => {\r\n\tconst rawMerge = mergePlacementDistributionsRaw(stats);\r\n\tconst result: { rank: number; percentage: number }[] = [];\r\n\tconst totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawMerge.find((d) => d.rank === i)?.totalMatches ?? 0;\r\n\t\tresult.push({ rank: i, percentage: round((100 * total) / totalDataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributionsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst rawStats = stats.map((stat) => stat.placementDistributionRaw);\r\n\t// console.debug(\r\n\t// \t'will merge placement distributions',\r\n\t// \tstats[0].heroCardId,\r\n\t// \tstats[0],\r\n\t// \trawStats.length,\r\n\t// \trawStats.filter((s) => !!s).length,\r\n\t// );\r\n\t// Legacy, can be removed after 2024-01-31\r\n\t// const pStats = stats\r\n\t// \t.filter((stat) => stat.placementDistribution?.length)\r\n\t// \t.map((stat) =>\r\n\t// \t\tstat.placementDistribution.map((s) => ({\r\n\t// \t\t\trank: s.rank,\r\n\t// \t\t\ttotalMatches: s.percentage * stat.dataPoints,\r\n\t// \t\t})),\r\n\t// \t);\r\n\t// const allRawStats = [...rawStats, ...pStats].filter((s) => !!s);\r\n\tconst result: { rank: number; totalMatches: number }[] = [];\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawStats\r\n\t\t\t.map((d) => d?.find((info) => info.rank === i)?.totalMatches ?? 0)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ rank: i, totalMatches: total });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStats = (stats: readonly BgsGlobalHeroStat[]): readonly { turn: number; averageStats: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);\r\n\tconst result: { turn: number; averageStats: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statsForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, averageStats: round(statsForTurn.totalStats / statsForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStatsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalStats: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!s?.length);\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalStats: number = rawStatsForTurn.map((d) => d?.totalStats ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });\r\n\t}\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst mergeCombatWinrate = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): readonly { turn: number; winrate: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);\r\n\tconst result: { turn: number; winrate: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, winrate: round(statForTurn.totalWinrate / statForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeCombatWinrateRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalWinrate: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!stat?.length);\r\n\t// debug && console.log('rawStats', rawStats?.length, rawStats);\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\t// debug && console.log('maxTurn', maxTurn);\r\n\tfor (let i = 0; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalWinrate: number = rawStatsForTurn.map((d) => d?.totalWinrate ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });\r\n\t}\r\n\t// debug && console.log('result', result);\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst getMaxTurn = (rawStats: readonly { turn: number }[]): number => {\r\n\tif (!rawStats?.length) {\r\n\t\treturn 0;\r\n\t}\r\n\treturn Math.max(...rawStats.map((stat) => stat.turn));\r\n};\r\n"]}
1
+ {"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { round } from '../../../common/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, BgsHeroTribeStat, MmrPercentileFilter } from '../../../models';\r\n\r\nexport const mergeStats = (\r\n\thourlyData: readonly BgsHeroStatsV2[],\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst allStats: readonly BgsGlobalHeroStat[] = hourlyData\r\n\t\t.flatMap((data) => data.heroStats)\r\n\t\t.filter((stat) => stat.mmrPercentile === mmrPercentile)\r\n\t\t.filter((stat) => stat.heroCardId !== CardIds.BaconphheroHeroic);\r\n\t// console.debug('allStats', mmrPercentile, allStats.length, '/', hourlyData.length);\r\n\tconst groupedByHero: {\r\n\t\t[heroCardId: string]: readonly BgsGlobalHeroStat[];\r\n\t} = groupByFunction((stat: BgsGlobalHeroStat) => stat.heroCardId)(allStats);\r\n\tconst result: readonly BgsGlobalHeroStat[] = Object.values(groupedByHero).map((stats) =>\r\n\t\tmergeStatsForSingleHero(stats, allCards),\r\n\t);\r\n\tconst maxDataPoints = Math.max(...result.map((r) => r.dataPoints));\r\n\tconst filtered = result.filter((r) => r.dataPoints > maxDataPoints / 50);\r\n\treturn filtered;\r\n};\r\n\r\nconst mergeStatsForSingleHero = (stats: readonly BgsGlobalHeroStat[], allCards: AllCardsService): BgsGlobalHeroStat => {\r\n\tconst ref = stats[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_010';\r\n\r\n\tconst totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst averagePosition = totalWeightedAverage / totalDataPoints;\r\n\r\n\tconst totalWeightedVariance = stats\r\n\t\t.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)\r\n\t\t.reduce((a, b) => a + b, 0);\r\n\tconst overallVariance = totalWeightedVariance / totalDataPoints;\r\n\tconst standardDeviation = Math.sqrt(overallVariance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);\r\n\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\ttotalOffered: stats.map((stat) => stat.totalOffered ?? 0).reduce((a, b) => a + b, 0),\r\n\t\ttotalPicked: stats.map((stat) => stat.totalPicked ?? 0).reduce((a, b) => a + b, 0),\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistribution: mergePlacementDistributions(stats),\r\n\t\twarbandStats: mergeWarbandStats(stats),\r\n\t\tcombatWinrate: mergeCombatWinrate(stats, debug),\r\n\t\ttribeStats: mergeTribeStats(stats, averagePosition).filter(\r\n\t\t\t(s) => s.dataPointsOnMissingTribe > s.dataPoints / 20,\r\n\t\t),\r\n\t\tanomalyStats: [], // mergeAnomalyStats(stats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst mergeTribeStats = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst allTribeStats = stats.flatMap((stat) => stat.tribeStats);\r\n\tconst uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));\r\n\tconst result: BgsHeroTribeStat[] = [...uniqueTribes].map((tribe) => {\r\n\t\tconst tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);\r\n\t\tconst averagePosition =\r\n\t\t\ttribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst averagePositionWithoutTribe =\r\n\t\t\ttribeStats\r\n\t\t\t\t.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePosition =\r\n\t\t// \ttribeStats.map((stat) => stat.impactAveragePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t// \ttribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePositionVsMissingTribe =\r\n\t\t// \ttribeStats\r\n\t\t// \t\t.map((stat) => stat.impactAveragePositionVsMissingTribe * stat.dataPoints)\r\n\t\t// \t\t.reduce((a, b) => a + b, 0) / tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\t\tdataPointsOnMissingTribe: tribeStats\r\n\t\t\t\t.map((stat) => stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0),\r\n\t\t\ttotalOffered: tribeStats.map((stat) => stat.totalOffered ?? 0).reduce((a, b) => a + b, 0),\r\n\t\t\ttotalPicked: tribeStats.map((stat) => stat.totalPicked ?? 0).reduce((a, b) => a + b, 0),\r\n\t\t\taveragePosition: round(averagePosition),\r\n\t\t\taveragePositionWithoutTribe: round(averagePositionWithoutTribe),\r\n\t\t\trefAveragePosition: round(refAveragePosition),\r\n\t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n\t\t\timpactAveragePositionVsMissingTribe: round(averagePosition - averagePositionWithoutTribe),\r\n\t\t};\r\n\t});\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributions = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; percentage: number }[] => {\r\n\tconst rawMerge = mergePlacementDistributionsRaw(stats);\r\n\tconst result: { rank: number; percentage: number }[] = [];\r\n\tconst totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawMerge.find((d) => d.rank === i)?.totalMatches ?? 0;\r\n\t\tresult.push({ rank: i, percentage: round((100 * total) / totalDataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributionsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst rawStats = stats.map((stat) => stat.placementDistributionRaw);\r\n\t// console.debug(\r\n\t// \t'will merge placement distributions',\r\n\t// \tstats[0].heroCardId,\r\n\t// \tstats[0],\r\n\t// \trawStats.length,\r\n\t// \trawStats.filter((s) => !!s).length,\r\n\t// );\r\n\t// Legacy, can be removed after 2024-01-31\r\n\t// const pStats = stats\r\n\t// \t.filter((stat) => stat.placementDistribution?.length)\r\n\t// \t.map((stat) =>\r\n\t// \t\tstat.placementDistribution.map((s) => ({\r\n\t// \t\t\trank: s.rank,\r\n\t// \t\t\ttotalMatches: s.percentage * stat.dataPoints,\r\n\t// \t\t})),\r\n\t// \t);\r\n\t// const allRawStats = [...rawStats, ...pStats].filter((s) => !!s);\r\n\tconst result: { rank: number; totalMatches: number }[] = [];\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawStats\r\n\t\t\t.map((d) => d?.find((info) => info.rank === i)?.totalMatches ?? 0)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ rank: i, totalMatches: total });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStats = (stats: readonly BgsGlobalHeroStat[]): readonly { turn: number; averageStats: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);\r\n\tconst result: { turn: number; averageStats: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statsForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, averageStats: round(statsForTurn.totalStats / statsForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStatsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalStats: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!s?.length);\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalStats: number = rawStatsForTurn.map((d) => d?.totalStats ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });\r\n\t}\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst mergeCombatWinrate = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): readonly { turn: number; winrate: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);\r\n\tconst result: { turn: number; winrate: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, winrate: round(statForTurn.totalWinrate / statForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeCombatWinrateRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalWinrate: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!stat?.length);\r\n\t// debug && console.log('rawStats', rawStats?.length, rawStats);\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\t// debug && console.log('maxTurn', maxTurn);\r\n\tfor (let i = 0; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalWinrate: number = rawStatsForTurn.map((d) => d?.totalWinrate ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });\r\n\t}\r\n\t// debug && console.log('result', result);\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst getMaxTurn = (rawStats: readonly { turn: number }[]): number => {\r\n\tif (!rawStats?.length) {\r\n\t\treturn 0;\r\n\t}\r\n\treturn Math.max(...rawStats.map((stat) => stat.turn));\r\n};\r\n"]}
@@ -24,7 +24,7 @@ const buildSingleTrinketStat = (data, allCards) => {
24
24
  const pickRateAtMmr = _build_aggregated_stats_1.mmrPercentiles.map((percentile) => {
25
25
  const allData = data.filter((d) => d.mmrPercentile === percentile);
26
26
  const pickedTotal = allData.map((d) => d.dataPoints).reduce((a, b) => a + b, 0);
27
- const offeredTotal = allData.map((d) => d.totalOffered).reduce((a, b) => a + b, 0);
27
+ const offeredTotal = allData.map((d) => { var _a; return (_a = d.totalOffered) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0);
28
28
  const pickRate = pickedTotal / offeredTotal;
29
29
  return {
30
30
  mmr: percentile,
@@ -1 +1 @@
1
- {"version":3,"file":"stats-builder.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/trinkets/stats-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { average } from '../../../common/util-functions';\r\nimport { InternalBgsGlobalTrinketStat, InternalBgsTrinketStats } from '../../../internal-model';\r\nimport { BgsTrinketStat } from '../../../model-trinkets';\r\nimport { WithMmrAndTimePeriod } from '../../../models';\r\nimport { mmrPercentiles } from './_build-aggregated-stats';\r\n\r\nexport const buildTrinketStats = (\r\n\thourlyData: readonly InternalBgsTrinketStats[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsTrinketStat[] => {\r\n\tconst allTrinketStats: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[] = hourlyData.flatMap(\r\n\t\t(data) => data.trinketStats,\r\n\t);\r\n\tconst groupedByTrinket = groupByFunction(\r\n\t\t(data: WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>) => data.trinketCardId,\r\n\t)(allTrinketStats);\r\n\treturn Object.keys(groupedByTrinket).map((trinketCardId) =>\r\n\t\tbuildSingleTrinketStat(groupedByTrinket[trinketCardId], allCards),\r\n\t);\r\n};\r\n\r\nconst buildSingleTrinketStat = (\r\n\tdata: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[],\r\n\tallCards: AllCardsService,\r\n): BgsTrinketStat => {\r\n\tconst ref = data[0];\r\n\tconst placementByMmr: readonly {\r\n\t\tmmr: number;\r\n\t\tdataPoints: number;\r\n\t\tplacement: number;\r\n\t}[] = mmrPercentiles.map((percentile) => {\r\n\t\tconst allData = data.filter((d) => d.mmrPercentile === percentile);\r\n\t\tconst averagePlacement = average(allData.map((d) => d.averagePlacement));\r\n\t\treturn {\r\n\t\t\tmmr: percentile,\r\n\t\t\tdataPoints: allData.map((d) => d.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\t\tplacement: averagePlacement,\r\n\t\t};\r\n\t});\r\n\tconst pickRateAtMmr: readonly {\r\n\t\tmmr: number;\r\n\t\tdataPoints: number;\r\n\t\tpickRate: number;\r\n\t}[] = mmrPercentiles.map((percentile) => {\r\n\t\tconst allData = data.filter((d) => d.mmrPercentile === percentile);\r\n\t\tconst pickedTotal = allData.map((d) => d.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst offeredTotal = allData.map((d) => d.totalOffered).reduce((a, b) => a + b, 0);\r\n\t\tconst pickRate = pickedTotal / offeredTotal;\r\n\t\treturn {\r\n\t\t\tmmr: percentile,\r\n\t\t\tdataPoints: pickedTotal,\r\n\t\t\tpickRate: pickRate,\r\n\t\t};\r\n\t});\r\n\r\n\tconst result: BgsTrinketStat = {\r\n\t\ttrinketCardId: ref.trinketCardId,\r\n\t\tdataPoints: pickRateAtMmr.find((d) => d.mmr === 100).dataPoints,\r\n\t\tpickRate: pickRateAtMmr.find((d) => d.mmr === 100).pickRate,\r\n\t\taveragePlacement: placementByMmr.find((d) => d.mmr === 100).placement,\r\n\t\taveragePlacementAtMmr: placementByMmr,\r\n\t\tpickRateAtMmr: pickRateAtMmr,\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
1
+ {"version":3,"file":"stats-builder.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/trinkets/stats-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { average } from '../../../common/util-functions';\r\nimport { InternalBgsGlobalTrinketStat, InternalBgsTrinketStats } from '../../../internal-model';\r\nimport { BgsTrinketStat } from '../../../model-trinkets';\r\nimport { WithMmrAndTimePeriod } from '../../../models';\r\nimport { mmrPercentiles } from './_build-aggregated-stats';\r\n\r\nexport const buildTrinketStats = (\r\n\thourlyData: readonly InternalBgsTrinketStats[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsTrinketStat[] => {\r\n\tconst allTrinketStats: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[] = hourlyData.flatMap(\r\n\t\t(data) => data.trinketStats,\r\n\t);\r\n\tconst groupedByTrinket = groupByFunction(\r\n\t\t(data: WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>) => data.trinketCardId,\r\n\t)(allTrinketStats);\r\n\treturn Object.keys(groupedByTrinket).map((trinketCardId) =>\r\n\t\tbuildSingleTrinketStat(groupedByTrinket[trinketCardId], allCards),\r\n\t);\r\n};\r\n\r\nconst buildSingleTrinketStat = (\r\n\tdata: readonly WithMmrAndTimePeriod<InternalBgsGlobalTrinketStat>[],\r\n\tallCards: AllCardsService,\r\n): BgsTrinketStat => {\r\n\tconst ref = data[0];\r\n\tconst placementByMmr: readonly {\r\n\t\tmmr: number;\r\n\t\tdataPoints: number;\r\n\t\tplacement: number;\r\n\t}[] = mmrPercentiles.map((percentile) => {\r\n\t\tconst allData = data.filter((d) => d.mmrPercentile === percentile);\r\n\t\tconst averagePlacement = average(allData.map((d) => d.averagePlacement));\r\n\t\treturn {\r\n\t\t\tmmr: percentile,\r\n\t\t\tdataPoints: allData.map((d) => d.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\t\tplacement: averagePlacement,\r\n\t\t};\r\n\t});\r\n\tconst pickRateAtMmr: readonly {\r\n\t\tmmr: number;\r\n\t\tdataPoints: number;\r\n\t\tpickRate: number;\r\n\t}[] = mmrPercentiles.map((percentile) => {\r\n\t\tconst allData = data.filter((d) => d.mmrPercentile === percentile);\r\n\t\tconst pickedTotal = allData.map((d) => d.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst offeredTotal = allData.map((d) => d.totalOffered ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst pickRate = pickedTotal / offeredTotal;\r\n\t\treturn {\r\n\t\t\tmmr: percentile,\r\n\t\t\tdataPoints: pickedTotal,\r\n\t\t\tpickRate: pickRate,\r\n\t\t};\r\n\t});\r\n\r\n\tconst result: BgsTrinketStat = {\r\n\t\ttrinketCardId: ref.trinketCardId,\r\n\t\tdataPoints: pickRateAtMmr.find((d) => d.mmr === 100).dataPoints,\r\n\t\tpickRate: pickRateAtMmr.find((d) => d.mmr === 100).pickRate,\r\n\t\taveragePlacement: placementByMmr.find((d) => d.mmr === 100).placement,\r\n\t\taveragePlacementAtMmr: placementByMmr,\r\n\t\tpickRateAtMmr: pickRateAtMmr,\r\n\t};\r\n\treturn result;\r\n};\r\n"]}
@@ -6,27 +6,32 @@ const util_functions_1 = require("../../common/util-functions");
6
6
  const builders_1 = require("./builders");
7
7
  const utils_1 = require("./utils");
8
8
  const buildHeroStatsForMmr = (rows, allCards) => {
9
- const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((row) => (0, util_functions_1.normalizeHeroCardId)(row.heroCardId, allCards))(rows);
10
- return Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));
9
+ const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((row) => row.heroCardId)(rows);
10
+ return Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data, rows));
11
11
  };
12
12
  exports.buildHeroStatsForMmr = buildHeroStatsForMmr;
13
- const buildStatsForSingleHero = (rows) => {
14
- const ref = rows[0];
15
- const averagePosition = average(rows.map((r) => r.playerRank));
16
- const placementDistribution = (0, utils_1.buildPlacementDistribution)(rows);
17
- const rawCombatWinrates = (0, builders_1.buildCombatWinrate)(rows);
18
- const rawWarbandStats = (0, builders_1.buildWarbandStats)(rows);
19
- const allRanks = rows.map((r) => r.playerRank);
13
+ const buildStatsForSingleHero = (rowsForHero, allRows) => {
14
+ const offeredRows = allRows.filter((r) => r.heroesOptionsExpanded.includes(rowsForHero[0].heroCardId));
15
+ const totalOffered = offeredRows.length;
16
+ const totalPicked = offeredRows.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;
17
+ const ref = rowsForHero[0];
18
+ const averagePosition = average(rowsForHero.map((r) => r.playerRank));
19
+ const placementDistribution = (0, utils_1.buildPlacementDistribution)(rowsForHero);
20
+ const rawCombatWinrates = (0, builders_1.buildCombatWinrate)(rowsForHero);
21
+ const rawWarbandStats = (0, builders_1.buildWarbandStats)(rowsForHero);
22
+ const allRanks = rowsForHero.map((r) => r.playerRank);
20
23
  const allDeviations = allRanks.map((r) => averagePosition - r);
21
24
  const squareDeviations = allDeviations.map((d) => Math.pow(d, 2));
22
25
  const sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);
23
- const variance = sumOfSquares / rows.length;
26
+ const variance = sumOfSquares / rowsForHero.length;
24
27
  const standardDeviation = Math.sqrt(variance);
25
- const standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);
26
- const tribeStats = buildTribeStats(rows, averagePosition);
28
+ const standardDeviationOfTheMean = standardDeviation / Math.sqrt(rowsForHero.length);
29
+ const tribeStats = buildTribeStats(rowsForHero, offeredRows, averagePosition);
27
30
  const result = {
28
31
  heroCardId: ref.heroCardId,
29
- dataPoints: rows.length,
32
+ dataPoints: rowsForHero.length,
33
+ totalOffered: totalOffered,
34
+ totalPicked: totalPicked,
30
35
  averagePosition: (0, util_functions_1.round)(averagePosition),
31
36
  standardDeviation: (0, util_functions_1.round)(standardDeviation),
32
37
  standardDeviationOfTheMean: (0, util_functions_1.round)(standardDeviationOfTheMean),
@@ -39,17 +44,22 @@ const buildStatsForSingleHero = (rows) => {
39
44
  };
40
45
  return result;
41
46
  };
42
- const buildTribeStats = (rows, refAveragePosition) => {
43
- const uniqueTribes = [...new Set(rows.flatMap((r) => r.tribesExpanded))];
47
+ const buildTribeStats = (rowsForHero, offeredRows, refAveragePosition) => {
48
+ const uniqueTribes = [...new Set(rowsForHero.flatMap((r) => r.tribesExpanded))];
44
49
  return uniqueTribes.map((tribe) => {
45
- const rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));
46
- const rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));
50
+ const rowsForTribe = rowsForHero.filter((r) => r.tribesExpanded.includes(tribe));
51
+ const offeredRowsForTribe = offeredRows.filter((r) => r.tribesExpanded.includes(tribe));
52
+ const totalOffered = offeredRowsForTribe.length;
53
+ const totalPicked = offeredRowsForTribe.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;
54
+ const rowsWithoutTribe = rowsForHero.filter((r) => !r.tribesExpanded.includes(tribe));
47
55
  const averagePosition = average(rowsForTribe.map((r) => r.playerRank));
48
56
  const averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));
49
57
  const result = {
50
58
  tribe: tribe,
51
59
  dataPoints: rowsForTribe.length,
52
60
  dataPointsOnMissingTribe: rowsWithoutTribe.length,
61
+ totalOffered: totalOffered,
62
+ totalPicked: totalPicked,
53
63
  averagePosition: averagePosition,
54
64
  averagePositionWithoutTribe: averagePositionWithoutTribe,
55
65
  impactAveragePosition: averagePosition - refAveragePosition,
@@ -1 +1 @@
1
- {"version":3,"file":"hero-stats-buikder.js","sourceRoot":"","sources":["../../../src/solo/hourly/hero-stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { normalizeHeroCardId, round } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from '../../models';\r\nimport { buildCombatWinrate, buildWarbandStats } from './builders';\r\nimport { buildPlacementDistribution } from './utils';\r\n\r\nexport const buildHeroStatsForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\t// This takes about 3s, so not impactful\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => normalizeHeroCardId(row.heroCardId, allCards))(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (rows: readonly InternalBgsRow[]): BgsGlobalHeroStat => {\r\n\t// const startTime = new Date().getTime();\r\n\tconst ref = rows[0];\r\n\tconst averagePosition = average(rows.map((r) => r.playerRank));\r\n\t// const placementStartTime = new Date().getTime();\r\n\tconst placementDistribution = buildPlacementDistribution(rows);\r\n\t// const placementProcessTime = new Date().getTime() - placementStartTime;\r\n\t// const winrateStartTime = new Date().getTime();\r\n\tconst rawCombatWinrates = buildCombatWinrate(rows);\r\n\t// const winrateProcessTime = new Date().getTime() - winrateStartTime;\r\n\t// const combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \twinrate: round(info.totalWinrate / info.dataPoints),\r\n\t// }));\r\n\tconst rawWarbandStats = buildWarbandStats(rows);\r\n\t// const warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \taverageStats: round(info.totalStats / info.dataPoints),\r\n\t// }));\r\n\r\n\tconst allRanks = rows.map((r) => r.playerRank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rows.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);\r\n\t// const tribeStartTime = new Date().getTime();\r\n\tconst tribeStats = buildTribeStats(rows, averagePosition);\r\n\t// const tribeProcessTime = new Date().getTime() - tribeStartTime;\r\n\t// const anomalyStartTime = new Date().getTime();\r\n\t// const anomalyStats = buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats);\r\n\t// const anomalyProcessTime = new Date().getTime() - anomalyStartTime;\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rows.length,\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistributionRaw: placementDistribution,\r\n\t\tcombatWinrateRaw: rawCombatWinrates,\r\n\t\twarbandStatsRaw: rawWarbandStats,\r\n\t\ttribeStats: tribeStats,\r\n\t\tanomalyStats: null, //anomalyStats,\r\n\t};\r\n\t// const processTime = new Date().getTime() - startTime;\r\n\t// console.log('\\tbuilt for hero', result.heroCardId, result.dataPoints, result);\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (rows: readonly InternalBgsRow[], refAveragePosition: number): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rows.flatMap((r) => r.tribesExpanded))];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.playerRank));\r\n\t\tconst averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));\r\n\t\tconst result: BgsHeroTribeStat = {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\taveragePositionWithoutTribe: averagePositionWithoutTribe,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\timpactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,\r\n\t\t};\r\n\t\treturn result;\r\n\t});\r\n};\r\n\r\n// const buildAnomalyStats = (\r\n// \trows: readonly InternalBgsRow[],\r\n// \trefAveragePosition: number,\r\n// \trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n// \trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n// \trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n// ): readonly BgsHeroAnomalyStat[] => {\r\n// \tconst rowsWithAnomalies = rows.filter((r) => !!r.bgsAnomalies?.length);\r\n// \tconst uniqueAnomalies: readonly string[] = [...new Set(rowsWithAnomalies.flatMap((r) => r.bgsAnomalies))];\r\n// \treturn uniqueAnomalies.map((anomaly) => {\r\n// \t\tconst rowsForAnomaly = rowsWithAnomalies.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n// \t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n// \t\tconst placementDistribution = buildPlacementDistribution(rowsForAnomaly);\r\n// \t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n// \t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\twinrate: round(info.totalWinrate / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n// \t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\taverageStats: round(info.totalStats / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst result: BgsHeroAnomalyStat = {\r\n// \t\t\tanomaly: anomaly,\r\n// \t\t\tdataPoints: rowsForAnomaly.length,\r\n// \t\t\taveragePosition: round(averagePosition),\r\n// \t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n// \t\t\tplacementDistributionRaw: placementDistribution,\r\n// \t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n// \t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n// \t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n// \t\t\t\tif (!newPlacementInfo) {\r\n// \t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\trank: p.rank,\r\n// \t\t\t\t\timpact: round((newPlacementInfo?.percentage ?? 0) - p.percentage),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\tcombatWinrate: combatWinrate,\r\n// \t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n// \t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newCombatWinrate) {\r\n// \t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newCombatWinrate?.winrate ?? 0) - c.winrate),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\twarbandStats: warbandStats,\r\n// \t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n// \t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newWarbandStats) {\r\n// \t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newWarbandStats?.averageStats ?? 0) - c.averageStats),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t};\r\n// \t\treturn result;\r\n// \t});\r\n// };\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
1
+ {"version":3,"file":"hero-stats-buikder.js","sourceRoot":"","sources":["../../../src/solo/hourly/hero-stats-buikder.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { round } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from '../../models';\r\nimport { buildCombatWinrate, buildWarbandStats } from './builders';\r\nimport { buildPlacementDistribution } from './utils';\r\n\r\nexport const buildHeroStatsForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\t// This takes about 3s, so not impactful\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => row.heroCardId)(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data, rows));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (\r\n\trowsForHero: readonly InternalBgsRow[],\r\n\tallRows: readonly InternalBgsRow[],\r\n): BgsGlobalHeroStat => {\r\n\t// const startTime = new Date().getTime();\r\n\tconst offeredRows = allRows.filter((r) => r.heroesOptionsExpanded.includes(rowsForHero[0].heroCardId));\r\n\tconst totalOffered = offeredRows.length;\r\n\tconst totalPicked = offeredRows.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;\r\n\tconst ref = rowsForHero[0];\r\n\tconst averagePosition = average(rowsForHero.map((r) => r.playerRank));\r\n\t// const placementStartTime = new Date().getTime();\r\n\tconst placementDistribution = buildPlacementDistribution(rowsForHero);\r\n\t// const placementProcessTime = new Date().getTime() - placementStartTime;\r\n\t// const winrateStartTime = new Date().getTime();\r\n\tconst rawCombatWinrates = buildCombatWinrate(rowsForHero);\r\n\t// const winrateProcessTime = new Date().getTime() - winrateStartTime;\r\n\t// const combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \twinrate: round(info.totalWinrate / info.dataPoints),\r\n\t// }));\r\n\tconst rawWarbandStats = buildWarbandStats(rowsForHero);\r\n\t// const warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \taverageStats: round(info.totalStats / info.dataPoints),\r\n\t// }));\r\n\r\n\tconst allRanks = rowsForHero.map((r) => r.playerRank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rowsForHero.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rowsForHero.length);\r\n\t// const tribeStartTime = new Date().getTime();\r\n\tconst tribeStats = buildTribeStats(rowsForHero, offeredRows, averagePosition);\r\n\t// const tribeProcessTime = new Date().getTime() - tribeStartTime;\r\n\t// const anomalyStartTime = new Date().getTime();\r\n\t// const anomalyStats = buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats);\r\n\t// const anomalyProcessTime = new Date().getTime() - anomalyStartTime;\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rowsForHero.length,\r\n\t\ttotalOffered: totalOffered,\r\n\t\ttotalPicked: totalPicked,\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistributionRaw: placementDistribution,\r\n\t\tcombatWinrateRaw: rawCombatWinrates,\r\n\t\twarbandStatsRaw: rawWarbandStats,\r\n\t\ttribeStats: tribeStats,\r\n\t\tanomalyStats: null, //anomalyStats,\r\n\t};\r\n\t// const processTime = new Date().getTime() - startTime;\r\n\t// console.log('\\tbuilt for hero', result.heroCardId, result.dataPoints, result);\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (\r\n\trowsForHero: readonly InternalBgsRow[],\r\n\tofferedRows: readonly InternalBgsRow[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rowsForHero.flatMap((r) => r.tribesExpanded))];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rowsForHero.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst offeredRowsForTribe = offeredRows.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst totalOffered = offeredRowsForTribe.length;\r\n\t\tconst totalPicked = offeredRowsForTribe.filter((r) => r.heroCardId === rowsForHero[0].heroCardId).length;\r\n\t\tconst rowsWithoutTribe = rowsForHero.filter((r) => !r.tribesExpanded.includes(tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.playerRank));\r\n\t\tconst averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.playerRank));\r\n\t\tconst result: BgsHeroTribeStat = {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\ttotalOffered: totalOffered,\r\n\t\t\ttotalPicked: totalPicked,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\taveragePositionWithoutTribe: averagePositionWithoutTribe,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\timpactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,\r\n\t\t};\r\n\t\treturn result;\r\n\t});\r\n};\r\n\r\n// const buildAnomalyStats = (\r\n// \trows: readonly InternalBgsRow[],\r\n// \trefAveragePosition: number,\r\n// \trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n// \trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n// \trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n// ): readonly BgsHeroAnomalyStat[] => {\r\n// \tconst rowsWithAnomalies = rows.filter((r) => !!r.bgsAnomalies?.length);\r\n// \tconst uniqueAnomalies: readonly string[] = [...new Set(rowsWithAnomalies.flatMap((r) => r.bgsAnomalies))];\r\n// \treturn uniqueAnomalies.map((anomaly) => {\r\n// \t\tconst rowsForAnomaly = rowsWithAnomalies.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n// \t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n// \t\tconst placementDistribution = buildPlacementDistribution(rowsForAnomaly);\r\n// \t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n// \t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\twinrate: round(info.totalWinrate / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n// \t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\taverageStats: round(info.totalStats / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst result: BgsHeroAnomalyStat = {\r\n// \t\t\tanomaly: anomaly,\r\n// \t\t\tdataPoints: rowsForAnomaly.length,\r\n// \t\t\taveragePosition: round(averagePosition),\r\n// \t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n// \t\t\tplacementDistributionRaw: placementDistribution,\r\n// \t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n// \t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n// \t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n// \t\t\t\tif (!newPlacementInfo) {\r\n// \t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\trank: p.rank,\r\n// \t\t\t\t\timpact: round((newPlacementInfo?.percentage ?? 0) - p.percentage),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\tcombatWinrate: combatWinrate,\r\n// \t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n// \t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newCombatWinrate) {\r\n// \t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newCombatWinrate?.winrate ?? 0) - c.winrate),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\twarbandStats: warbandStats,\r\n// \t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n// \t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newWarbandStats) {\r\n// \t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newWarbandStats?.averageStats ?? 0) - c.averageStats),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t};\r\n// \t\treturn result;\r\n// \t});\r\n// };\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
@@ -149,13 +149,16 @@ const processRows = async (rows, multipartUpload, allCards) => {
149
149
  row.heroCardId !== "BG22_HERO_007t")
150
150
  .filter((row) => { var _a; return !!row.playerRank && !!((_a = row.tribes) === null || _a === void 0 ? void 0 : _a.length); })
151
151
  .map((row) => {
152
+ var _a, _b;
152
153
  const result = {
153
154
  ...row,
154
155
  heroCardId: (0, util_functions_1.normalizeHeroCardId)(row.heroCardId, allCards),
155
156
  tribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),
157
+ heroesOptionsExpanded: (_b = (_a = row.heroesOptions) === null || _a === void 0 ? void 0 : _a.split(',').map((hero) => (0, util_functions_1.normalizeHeroCardId)(hero, allCards))) !== null && _b !== void 0 ? _b : [],
156
158
  };
157
159
  delete result.reviewId;
158
160
  delete result.tribes;
161
+ delete result.heroesOptions;
159
162
  return result;
160
163
  });
161
164
  if (validRows.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/solo/hourly/rows.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3Multipart } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { S3 as S3AWS } from 'aws-sdk';\r\nimport SecretsManager, { GetSecretValueRequest, GetSecretValueResponse } from 'aws-sdk/clients/secretsmanager';\r\nimport { Connection, createPool } from 'mysql';\r\nimport { Readable } from 'stream';\r\nimport { normalizeHeroCardId } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { STATS_BUCKET, WORKING_ROWS_FILE, s3 } from './_build-battlegrounds-hero-stats';\r\n\r\nexport const readRowsFromS3 = async (startDate: string): Promise<readonly InternalBgsRow[]> => {\r\n\treturn new Promise<readonly InternalBgsRow[]>((resolve, reject) => {\r\n\t\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate)}`;\r\n\t\tconsole.debug('reading rows from s3', workingRowsFile);\r\n\t\tlet parseErrors = 0;\r\n\t\tlet totalParsed = 0;\r\n\t\tconst stream: Readable = s3.readStream(STATS_BUCKET, workingRowsFile);\r\n\t\tconst result: InternalBgsRow[] = [];\r\n\t\tlet previousString = '';\r\n\t\tlet emptyRowsInARow = 0;\r\n\t\tstream\r\n\t\t\t.on('data', (chunk) => {\r\n\t\t\t\tconst str = Buffer.from(chunk).toString('utf-8');\r\n\t\t\t\tconst newStr = previousString + str;\r\n\t\t\t\tconst split = newStr.split('\\n');\r\n\t\t\t\tconst rows: readonly InternalBgsRow[] = split.slice(0, split.length - 1).map((row) => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst result: InternalBgsRow = JSON.parse(row);\r\n\t\t\t\t\t\ttotalParsed++;\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t} catch (e) {\r\n\t\t\t\t\t\t// logger.warn('could not parse row', row);\r\n\t\t\t\t\t\tparseErrors++;\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\tpreviousString = split[split.length - 1];\r\n\t\t\t\tresult.push(...rows);\r\n\r\n\t\t\t\t// Do this to avoid errors in case the chunks are small compared to the row sizes\r\n\t\t\t\tif (result.length === 0 && rows.length === 0) {\r\n\t\t\t\t\temptyRowsInARow++;\r\n\t\t\t\t} else {\r\n\t\t\t\t\temptyRowsInARow = 0;\r\n\t\t\t\t}\r\n\t\t\t\tif (emptyRowsInARow > 50) {\r\n\t\t\t\t\tconsole.error(newStr);\r\n\t\t\t\t\tconsole.error(split);\r\n\t\t\t\t\tthrow new Error('Could not parse any row');\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', () => {\r\n\t\t\t\tconst finalResult = result.filter((row) => !!row);\r\n\t\t\t\tconsole.log('stream end', result.length, finalResult.length);\r\n\t\t\t\tconsole.log('parsing errors', parseErrors, 'and successes', totalParsed);\r\n\t\t\t\tresolve(finalResult);\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nexport const saveRowsOnS3 = async (startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\tconsole.log('will export rows to S3', startDate, endDate);\r\n\tconst secretRequest: GetSecretValueRequest = {\r\n\t\tSecretId: 'rds-connection',\r\n\t};\r\n\tconst secret: SecretInfo = await getSecret(secretRequest);\r\n\tconst pool = createPool({\r\n\t\tconnectionLimit: 1,\r\n\t\thost: secret.hostReadOnly,\r\n\t\tuser: secret.username,\r\n\t\tpassword: secret.password,\r\n\t\tdatabase: 'replay_summary',\r\n\t\tport: secret.port,\r\n\t});\r\n\r\n\ttry {\r\n\t\tawait performRowProcessIngPool(pool, startDate, endDate, allCards);\r\n\t} finally {\r\n\t\tpool.end((err) => {\r\n\t\t\tconsole.log('ending pool', err);\r\n\t\t});\r\n\t}\r\n};\r\n\r\nconst performRowProcessIngPool = async (pool: any, startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tpool.getConnection(async (err, connection) => {\r\n\t\t\tif (err) {\r\n\t\t\t\tconsole.log('error with connection', err);\r\n\t\t\t\tthrow new Error('Could not connect to DB');\r\n\t\t\t} else {\r\n\t\t\t\tawait performRowsProcessing(connection, startDate, endDate, allCards);\r\n\t\t\t\tconnection.release();\r\n\t\t\t}\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n};\r\n\r\nconst performRowsProcessing = async (\r\n\tconnection: Connection,\r\n\tstartDate: Date,\r\n\tendDate: Date,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst multipartUpload = new S3Multipart(new S3AWS());\r\n\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate.toISOString())}`;\r\n\tawait multipartUpload.initMultipart('static.zerotoheroes.com', workingRowsFile, 'application/json');\r\n\tconsole.log('multipart upload init', workingRowsFile);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tconst queryStr = `\r\n\t\t\tSELECT * FROM bgs_run_stats\r\n\t\t\tWHERE creationDate >= ?\r\n\t\t\tAND creationDate < ?\r\n\t\t`;\r\n\t\tconsole.log('running query', queryStr);\r\n\t\tconst query = connection.query(queryStr, [startDate, endDate]);\r\n\r\n\t\tlet rowsToProcess = [];\r\n\t\tlet rowCount = 0;\r\n\t\tquery\r\n\t\t\t.on('error', (err) => {\r\n\t\t\t\tconsole.error('error while fetching rows', err);\r\n\t\t\t})\r\n\t\t\t.on('fields', (fields) => {\r\n\t\t\t\tconsole.log('fields', fields);\r\n\t\t\t})\r\n\t\t\t.on('result', async (row) => {\r\n\t\t\t\trowsToProcess.push(row);\r\n\t\t\t\tif (rowsToProcess.length > 20000 && !multipartUpload.processing) {\r\n\t\t\t\t\tconnection.pause();\r\n\t\t\t\t\t// console.log('before upload', rowsToProcess.length);\r\n\t\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\t\trowsToProcess = [];\r\n\t\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\t\trowCount += uploaded;\r\n\t\t\t\t\tconsole.log('processed rows', uploaded, '/', toUpload.length, rowCount);\r\n\t\t\t\t\tconnection.resume();\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', async () => {\r\n\t\t\t\tconsole.log('end');\r\n\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\trowsToProcess = [];\r\n\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\trowCount += uploaded;\r\n\t\t\t\tconsole.log('processed rows', uploaded, rowCount);\r\n\t\t\t\t// connection.resume();\r\n\t\t\t\tawait multipartUpload.completeMultipart();\r\n\t\t\t\tresolve();\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nconst processRows = async (\r\n\trows: readonly InternalBgsRow[],\r\n\tmultipartUpload: S3Multipart,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst validRows = rows\r\n\t\t// .filter((row) => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(\r\n\t\t\t(row) =>\r\n\t\t\t\trow.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedToken &&\r\n\t\t\t\trow.heroCardId !== CardIds.QueenAzshara_NagaQueenAzsharaToken,\r\n\t\t)\r\n\t\t.filter((row) => !!row.playerRank && !!row.tribes?.length)\r\n\t\t.map((row) => {\r\n\t\t\tconst result: InternalBgsRow = {\r\n\t\t\t\t...row,\r\n\t\t\t\theroCardId: normalizeHeroCardId(row.heroCardId, allCards),\r\n\t\t\t\ttribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),\r\n\t\t\t};\r\n\t\t\tdelete (result as any).reviewId;\r\n\t\t\tdelete (result as any).tribes;\r\n\t\t\treturn result;\r\n\t\t});\r\n\tif (validRows.length > 0) {\r\n\t\t// console.log('\\t', 'uploading', validRows.length, 'rows');\r\n\t\tawait multipartUpload.uploadPart(validRows.map((r) => JSON.stringify(r)).join('\\n'));\r\n\t}\r\n\treturn validRows.length;\r\n};\r\n\r\nconst getSecret = (secretRequest: GetSecretValueRequest) => {\r\n\tconst secretsManager = new SecretsManager({ region: 'us-west-2' });\r\n\treturn new Promise<SecretInfo>((resolve) => {\r\n\t\tsecretsManager.getSecretValue(secretRequest, (err, data: GetSecretValueResponse) => {\r\n\t\t\tconst secretInfo: SecretInfo = JSON.parse(data.SecretString);\r\n\t\t\tresolve(secretInfo);\r\n\t\t});\r\n\t});\r\n};\r\n\r\ninterface SecretInfo {\r\n\treadonly username: string;\r\n\treadonly password: string;\r\n\treadonly host: string;\r\n\treadonly hostReadOnly: string;\r\n\treadonly port: number;\r\n\treadonly dbClusterIdentifier: string;\r\n}\r\n"]}
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/solo/hourly/rows.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3Multipart } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { S3 as S3AWS } from 'aws-sdk';\r\nimport SecretsManager, { GetSecretValueRequest, GetSecretValueResponse } from 'aws-sdk/clients/secretsmanager';\r\nimport { Connection, createPool } from 'mysql';\r\nimport { Readable } from 'stream';\r\nimport { normalizeHeroCardId } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { STATS_BUCKET, WORKING_ROWS_FILE, s3 } from './_build-battlegrounds-hero-stats';\r\n\r\nexport const readRowsFromS3 = async (startDate: string): Promise<readonly InternalBgsRow[]> => {\r\n\treturn new Promise<readonly InternalBgsRow[]>((resolve, reject) => {\r\n\t\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate)}`;\r\n\t\tconsole.debug('reading rows from s3', workingRowsFile);\r\n\t\tlet parseErrors = 0;\r\n\t\tlet totalParsed = 0;\r\n\t\tconst stream: Readable = s3.readStream(STATS_BUCKET, workingRowsFile);\r\n\t\tconst result: InternalBgsRow[] = [];\r\n\t\tlet previousString = '';\r\n\t\tlet emptyRowsInARow = 0;\r\n\t\tstream\r\n\t\t\t.on('data', (chunk) => {\r\n\t\t\t\tconst str = Buffer.from(chunk).toString('utf-8');\r\n\t\t\t\tconst newStr = previousString + str;\r\n\t\t\t\tconst split = newStr.split('\\n');\r\n\t\t\t\tconst rows: readonly InternalBgsRow[] = split.slice(0, split.length - 1).map((row) => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst result: InternalBgsRow = JSON.parse(row);\r\n\t\t\t\t\t\ttotalParsed++;\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t} catch (e) {\r\n\t\t\t\t\t\t// logger.warn('could not parse row', row);\r\n\t\t\t\t\t\tparseErrors++;\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\tpreviousString = split[split.length - 1];\r\n\t\t\t\tresult.push(...rows);\r\n\r\n\t\t\t\t// Do this to avoid errors in case the chunks are small compared to the row sizes\r\n\t\t\t\tif (result.length === 0 && rows.length === 0) {\r\n\t\t\t\t\temptyRowsInARow++;\r\n\t\t\t\t} else {\r\n\t\t\t\t\temptyRowsInARow = 0;\r\n\t\t\t\t}\r\n\t\t\t\tif (emptyRowsInARow > 50) {\r\n\t\t\t\t\tconsole.error(newStr);\r\n\t\t\t\t\tconsole.error(split);\r\n\t\t\t\t\tthrow new Error('Could not parse any row');\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', () => {\r\n\t\t\t\tconst finalResult = result.filter((row) => !!row);\r\n\t\t\t\tconsole.log('stream end', result.length, finalResult.length);\r\n\t\t\t\tconsole.log('parsing errors', parseErrors, 'and successes', totalParsed);\r\n\t\t\t\tresolve(finalResult);\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nexport const saveRowsOnS3 = async (startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\tconsole.log('will export rows to S3', startDate, endDate);\r\n\tconst secretRequest: GetSecretValueRequest = {\r\n\t\tSecretId: 'rds-connection',\r\n\t};\r\n\tconst secret: SecretInfo = await getSecret(secretRequest);\r\n\tconst pool = createPool({\r\n\t\tconnectionLimit: 1,\r\n\t\thost: secret.hostReadOnly,\r\n\t\tuser: secret.username,\r\n\t\tpassword: secret.password,\r\n\t\tdatabase: 'replay_summary',\r\n\t\tport: secret.port,\r\n\t});\r\n\r\n\ttry {\r\n\t\tawait performRowProcessIngPool(pool, startDate, endDate, allCards);\r\n\t} finally {\r\n\t\tpool.end((err) => {\r\n\t\t\tconsole.log('ending pool', err);\r\n\t\t});\r\n\t}\r\n};\r\n\r\nconst performRowProcessIngPool = async (pool: any, startDate: Date, endDate: Date, allCards: AllCardsService) => {\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tpool.getConnection(async (err, connection) => {\r\n\t\t\tif (err) {\r\n\t\t\t\tconsole.log('error with connection', err);\r\n\t\t\t\tthrow new Error('Could not connect to DB');\r\n\t\t\t} else {\r\n\t\t\t\tawait performRowsProcessing(connection, startDate, endDate, allCards);\r\n\t\t\t\tconnection.release();\r\n\t\t\t}\r\n\t\t\tresolve();\r\n\t\t});\r\n\t});\r\n};\r\n\r\nconst performRowsProcessing = async (\r\n\tconnection: Connection,\r\n\tstartDate: Date,\r\n\tendDate: Date,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst multipartUpload = new S3Multipart(new S3AWS());\r\n\tconst workingRowsFile = `${WORKING_ROWS_FILE.replace('%time%', startDate.toISOString())}`;\r\n\tawait multipartUpload.initMultipart('static.zerotoheroes.com', workingRowsFile, 'application/json');\r\n\tconsole.log('multipart upload init', workingRowsFile);\r\n\r\n\treturn new Promise<void>((resolve) => {\r\n\t\tconst queryStr = `\r\n\t\t\tSELECT * FROM bgs_run_stats\r\n\t\t\tWHERE creationDate >= ?\r\n\t\t\tAND creationDate < ?\r\n\t\t`;\r\n\t\tconsole.log('running query', queryStr);\r\n\t\tconst query = connection.query(queryStr, [startDate, endDate]);\r\n\r\n\t\tlet rowsToProcess = [];\r\n\t\tlet rowCount = 0;\r\n\t\tquery\r\n\t\t\t.on('error', (err) => {\r\n\t\t\t\tconsole.error('error while fetching rows', err);\r\n\t\t\t})\r\n\t\t\t.on('fields', (fields) => {\r\n\t\t\t\tconsole.log('fields', fields);\r\n\t\t\t})\r\n\t\t\t.on('result', async (row) => {\r\n\t\t\t\trowsToProcess.push(row);\r\n\t\t\t\tif (rowsToProcess.length > 20000 && !multipartUpload.processing) {\r\n\t\t\t\t\tconnection.pause();\r\n\t\t\t\t\t// console.log('before upload', rowsToProcess.length);\r\n\t\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\t\trowsToProcess = [];\r\n\t\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\t\trowCount += uploaded;\r\n\t\t\t\t\tconsole.log('processed rows', uploaded, '/', toUpload.length, rowCount);\r\n\t\t\t\t\tconnection.resume();\r\n\t\t\t\t}\r\n\t\t\t})\r\n\t\t\t.on('end', async () => {\r\n\t\t\t\tconsole.log('end');\r\n\t\t\t\tconst toUpload = rowsToProcess;\r\n\t\t\t\trowsToProcess = [];\r\n\t\t\t\t// console.log('will upload', toUpload.length, 'rows');\r\n\t\t\t\tconst uploaded = await processRows(toUpload, multipartUpload, allCards);\r\n\t\t\t\trowCount += uploaded;\r\n\t\t\t\tconsole.log('processed rows', uploaded, rowCount);\r\n\t\t\t\t// connection.resume();\r\n\t\t\t\tawait multipartUpload.completeMultipart();\r\n\t\t\t\tresolve();\r\n\t\t\t});\r\n\t});\r\n};\r\n\r\nconst processRows = async (\r\n\trows: readonly InternalBgsRow[],\r\n\tmultipartUpload: S3Multipart,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst validRows = rows\r\n\t\t// .filter((row) => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(\r\n\t\t\t(row) =>\r\n\t\t\t\trow.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedToken &&\r\n\t\t\t\trow.heroCardId !== CardIds.QueenAzshara_NagaQueenAzsharaToken,\r\n\t\t)\r\n\t\t.filter((row) => !!row.playerRank && !!row.tribes?.length)\r\n\t\t.map((row) => {\r\n\t\t\tconst result: InternalBgsRow = {\r\n\t\t\t\t...row,\r\n\t\t\t\theroCardId: normalizeHeroCardId(row.heroCardId, allCards),\r\n\t\t\t\ttribesExpanded: row.tribes.split(',').map((tribe) => parseInt(tribe)),\r\n\t\t\t\theroesOptionsExpanded:\r\n\t\t\t\t\trow.heroesOptions?.split(',').map((hero) => normalizeHeroCardId(hero, allCards)) ?? [],\r\n\t\t\t};\r\n\t\t\tdelete (result as any).reviewId;\r\n\t\t\tdelete (result as any).tribes;\r\n\t\t\tdelete (result as any).heroesOptions;\r\n\t\t\treturn result;\r\n\t\t});\r\n\tif (validRows.length > 0) {\r\n\t\t// console.log('\\t', 'uploading', validRows.length, 'rows');\r\n\t\tawait multipartUpload.uploadPart(validRows.map((r) => JSON.stringify(r)).join('\\n'));\r\n\t}\r\n\treturn validRows.length;\r\n};\r\n\r\nconst getSecret = (secretRequest: GetSecretValueRequest) => {\r\n\tconst secretsManager = new SecretsManager({ region: 'us-west-2' });\r\n\treturn new Promise<SecretInfo>((resolve) => {\r\n\t\tsecretsManager.getSecretValue(secretRequest, (err, data: GetSecretValueResponse) => {\r\n\t\t\tconst secretInfo: SecretInfo = JSON.parse(data.SecretString);\r\n\t\t\tresolve(secretInfo);\r\n\t\t});\r\n\t});\r\n};\r\n\r\ninterface SecretInfo {\r\n\treadonly username: string;\r\n\treadonly password: string;\r\n\treadonly host: string;\r\n\treadonly hostReadOnly: string;\r\n\treadonly port: number;\r\n\treadonly dbClusterIdentifier: string;\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firestone-hs/bgs-global-stats",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "lint": "eslint --color --fix --ext .ts .",