@firestone-hs/bgs-global-stats 1.0.51 → 1.0.52

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.
Files changed (39) hide show
  1. package/dist/common/anomalies.d.ts +1 -0
  2. package/dist/common/anomalies.js +37 -0
  3. package/dist/common/anomalies.js.map +1 -0
  4. package/dist/duos/aggregate-hourly/heroes/stats-merger.js +0 -1
  5. package/dist/duos/aggregate-hourly/heroes/stats-merger.js.map +1 -1
  6. package/dist/duos/hourly/hero-stats-buikder.js +0 -1
  7. package/dist/duos/hourly/hero-stats-buikder.js.map +1 -1
  8. package/dist/models.d.ts +0 -35
  9. package/dist/models.js.map +1 -1
  10. package/dist/solo/aggregate-daily/cards/_build-aggregated-stats.js +15 -7
  11. package/dist/solo/aggregate-daily/cards/_build-aggregated-stats.js.map +1 -1
  12. package/dist/solo/aggregate-daily/cards/s3-saver.js +1 -1
  13. package/dist/solo/aggregate-daily/cards/s3-saver.js.map +1 -1
  14. package/dist/solo/aggregate-hourly/config.js +2 -2
  15. package/dist/solo/aggregate-hourly/config.js.map +1 -1
  16. package/dist/solo/aggregate-hourly/heroes/_build-aggregated-stats.js +37 -22
  17. package/dist/solo/aggregate-hourly/heroes/_build-aggregated-stats.js.map +1 -1
  18. package/dist/solo/aggregate-hourly/heroes/s3-saver.d.ts +2 -1
  19. package/dist/solo/aggregate-hourly/heroes/s3-saver.js +10 -4
  20. package/dist/solo/aggregate-hourly/heroes/s3-saver.js.map +1 -1
  21. package/dist/solo/aggregate-hourly/heroes/stats-merger.js +0 -1
  22. package/dist/solo/aggregate-hourly/heroes/stats-merger.js.map +1 -1
  23. package/dist/solo/aggregate-hourly/quests/_build-aggregated-stats.js +1 -1
  24. package/dist/solo/aggregate-hourly/quests/_build-aggregated-stats.js.map +1 -1
  25. package/dist/solo/aggregate-hourly/s3-loader.d.ts +2 -2
  26. package/dist/solo/aggregate-hourly/s3-loader.js +9 -7
  27. package/dist/solo/aggregate-hourly/s3-loader.js.map +1 -1
  28. package/dist/solo/aggregate-hourly/trinkets/_build-aggregated-stats.js +1 -1
  29. package/dist/solo/aggregate-hourly/trinkets/_build-aggregated-stats.js.map +1 -1
  30. package/dist/solo/final-stats/cards/_build-aggregated-stats.js +6 -0
  31. package/dist/solo/final-stats/cards/_build-aggregated-stats.js.map +1 -1
  32. package/dist/solo/hourly/_build-battlegrounds-hero-stats.js +12 -3
  33. package/dist/solo/hourly/_build-battlegrounds-hero-stats.js.map +1 -1
  34. package/dist/solo/hourly/hero-stats-buikder.js +0 -1
  35. package/dist/solo/hourly/hero-stats-buikder.js.map +1 -1
  36. package/dist/solo/hourly/hero-stats.d.ts +1 -1
  37. package/dist/solo/hourly/hero-stats.js +5 -2
  38. package/dist/solo/hourly/hero-stats.js.map +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ export declare const readAllAnomalies: (bucket: string, folder: string) => Promise<readonly (string | null)[]>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readAllAnomalies = void 0;
4
+ const aws_sdk_1 = require("aws-sdk");
5
+ const s3 = new aws_sdk_1.S3();
6
+ const readAllAnomalies = async (bucket, folder) => {
7
+ var _a, _b;
8
+ const params = {
9
+ Bucket: bucket,
10
+ Prefix: folder,
11
+ };
12
+ let allKeys = [];
13
+ let continuationToken;
14
+ do {
15
+ if (continuationToken) {
16
+ params.ContinuationToken = continuationToken;
17
+ }
18
+ try {
19
+ const data = await s3.listObjectsV2(params).promise();
20
+ const allKeysForToken = (_b = (_a = data.Contents) === null || _a === void 0 ? void 0 : _a.map((item) => item.Key)) !== null && _b !== void 0 ? _b : [];
21
+ const suffixes = allKeysForToken.map((key) => key.replace(`${folder}/`, ''));
22
+ const anomalies = suffixes.map((suffix) => suffix.split('/')[0]);
23
+ const uniqueAnomalies = [...new Set(anomalies)];
24
+ allKeys = allKeys.concat(uniqueAnomalies);
25
+ continuationToken = data.IsTruncated ? data.NextContinuationToken : undefined;
26
+ }
27
+ catch (error) {
28
+ console.error('Error listing files:', error);
29
+ return [null];
30
+ }
31
+ } while (continuationToken);
32
+ const uniqueKeys = [...new Set(allKeys)];
33
+ const result = [null, ...uniqueKeys];
34
+ return result;
35
+ };
36
+ exports.readAllAnomalies = readAllAnomalies;
37
+ //# sourceMappingURL=anomalies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anomalies.js","sourceRoot":"","sources":["../../src/common/anomalies.ts"],"names":[],"mappings":";;;AAAA,qCAA6B;AAE7B,MAAM,EAAE,GAAG,IAAI,YAAE,EAAE,CAAC;AAEb,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAAc,EAAE,MAAc,EAAuC,EAAE;;IAC7G,MAAM,MAAM,GAAkC;QAC7C,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,MAAM;KACd,CAAC;IACF,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,iBAAqC,CAAC;IAE1C,GAAG;QACF,IAAI,iBAAiB,EAAE;YACtB,MAAM,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;SAC7C;QAED,IAAI;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;YAEtD,MAAM,eAAe,GAAG,MAAA,MAAA,IAAI,CAAC,QAAQ,0CAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,mCAAI,EAAE,CAAC;YACrE,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,MAAM,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7E,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjE,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YAEhD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;YAC1C,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC;SAC9E;QAAC,OAAO,KAAK,EAAE;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;SACd;KACD,QAAQ,iBAAiB,EAAE;IAE5B,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAlCW,QAAA,gBAAgB,oBAkC3B","sourcesContent":["import { S3 } from 'aws-sdk';\r\n\r\nconst s3 = new S3();\r\n\r\nexport const readAllAnomalies = async (bucket: string, folder: string): Promise<readonly (string | null)[]> => {\r\n\tconst params: S3.Types.ListObjectsV2Request = {\r\n\t\tBucket: bucket,\r\n\t\tPrefix: folder,\r\n\t};\r\n\tlet allKeys: string[] = [];\r\n\tlet continuationToken: string | undefined;\r\n\r\n\tdo {\r\n\t\tif (continuationToken) {\r\n\t\t\tparams.ContinuationToken = continuationToken;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst data = await s3.listObjectsV2(params).promise();\r\n\t\t\t// console.debug('listing result', params.ContinuationToken, data.Contents);\r\n\t\t\tconst allKeysForToken = data.Contents?.map((item) => item.Key) ?? [];\r\n\t\t\tconst suffixes = allKeysForToken.map((key) => key.replace(`${folder}/`, ''));\r\n\t\t\tconst anomalies = suffixes.map((suffix) => suffix.split('/')[0]);\r\n\t\t\tconst uniqueAnomalies = [...new Set(anomalies)];\r\n\t\t\t// console.debug('uniqueAnomalies', uniqueAnomalies);\r\n\t\t\tallKeys = allKeys.concat(uniqueAnomalies);\r\n\t\t\tcontinuationToken = data.IsTruncated ? data.NextContinuationToken : undefined;\r\n\t\t} catch (error) {\r\n\t\t\tconsole.error('Error listing files:', error);\r\n\t\t\treturn [null];\r\n\t\t}\r\n\t} while (continuationToken);\r\n\r\n\tconst uniqueKeys = [...new Set(allKeys)];\r\n\t// console.debug('uniqueKeys', uniqueKeys);\r\n\tconst result = [null, ...uniqueKeys];\r\n\t// console.debug('listing anomalies', bucket, folder, result);\r\n\treturn result;\r\n};\r\n"]}
@@ -40,7 +40,6 @@ const mergeStatsForSingleHero = (stats, allCards) => {
40
40
  warbandStats: mergeWarbandStats(stats),
41
41
  combatWinrate: mergeCombatWinrate(stats, debug),
42
42
  tribeStats: mergeTribeStats(stats, averagePosition).filter((s) => s.dataPointsOnMissingTribe > s.dataPoints / 20),
43
- anomalyStats: [],
44
43
  };
45
44
  return result;
46
45
  };
@@ -1 +1 @@
1
- {"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":";;;AAAA,qEAAiE;AAEjE,mEAAuD;AAGhD,MAAM,UAAU,GAAG,CACzB,UAAqC,EACrC,aAAkC,EAClC,QAAyB,EACM,EAAE;IACjC,MAAM,QAAQ,GAAiC,UAAU;SACvD,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;SACjC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,KAAK,aAAa,CAAC;SACtD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,2BAA8B,CAAC,CAAC;IAElE,MAAM,aAAa,GAEf,IAAA,kCAAe,EAAC,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAiC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACvF,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,CACxC,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,aAAa,GAAG,EAAE,CAAC,CAAC;IACzE,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAnBW,QAAA,UAAU,cAmBrB;AAEF,MAAM,uBAAuB,GAAG,CAAC,KAAmC,EAAE,QAAyB,EAAqB,EAAE;IACrH,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,KAAK,eAAe,CAAC;IAEjD,MAAM,oBAAoB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3G,MAAM,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAClF,MAAM,eAAe,GAAG,oBAAoB,GAAG,eAAe,CAAC;IAE/D,MAAM,qBAAqB,GAAG,KAAK;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,iBAAiB,GAAG,CAAC,CAAC,UAAU,CAAC;SACpE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,eAAe,GAAG,qBAAqB,GAAG,eAAe,CAAC;IAChE,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACrD,MAAM,0BAA0B,GAAG,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAElF,MAAM,MAAM,GAAsB;QACjC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3E,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,YAAY,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpF,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,WAAW,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClF,eAAe,EAAE,IAAA,sBAAK,EAAC,eAAe,CAAC;QACvC,iBAAiB,EAAE,IAAA,sBAAK,EAAC,iBAAiB,CAAC;QAC3C,0BAA0B,EAAE,IAAA,sBAAK,EAAC,0BAA0B,CAAC;QAC7D,4BAA4B,EAAE,IAAA,sBAAK,EAAC,eAAe,GAAG,CAAC,GAAG,0BAA0B,CAAC;QACrF,qBAAqB,EAAE,2BAA2B,CAAC,KAAK,CAAC;QACzD,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC;QACtC,aAAa,EAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC;QAC/C,UAAU,EAAE,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,MAAM,CACzD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,wBAAwB,GAAG,CAAC,CAAC,UAAU,GAAG,EAAE,CACrD;QACD,YAAY,EAAE,EAAE;KAChB,CAAC;IACF,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CACvB,KAAmC,EACnC,kBAA0B,EACI,EAAE;IAChC,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,MAAM,MAAM,GAAuB,CAAC,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAClE,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QACxE,MAAM,mBAAmB,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjG,MAAM,eAAe,GACpB,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3F,mBAAmB,CAAC;QACrB,MAAM,2BAA2B,GAChC,UAAU;aACR,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,2BAA2B,GAAG,IAAI,CAAC,wBAAwB,CAAC;aAC/E,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5B,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAQpF,OAAO;YACN,KAAK,EAAE,KAAK;YACZ,UAAU,EAAE,mBAAmB;YAC/B,wBAAwB,EAAE,UAAU;iBAClC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC;iBAC5C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC5B,YAAY,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,YAAY,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzF,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,WAAW,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACvF,eAAe,EAAE,IAAA,sBAAK,EAAC,eAAe,CAAC;YACvC,2BAA2B,EAAE,IAAA,sBAAK,EAAC,2BAA2B,CAAC;YAC/D,kBAAkB,EAAE,kBAAkB;YACtC,qBAAqB,EAAE,IAAA,sBAAK,EAAC,eAAe,GAAG,kBAAkB,CAAC;YAElE,mCAAmC,EAAE,IAAA,sBAAK,EAAC,eAAe,GAAG,2BAA2B,CAAC;SACzF,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CACnC,KAAmC,EACe,EAAE;;IACpD,MAAM,QAAQ,GAAG,8BAA8B,CAAC,KAAK,CAAC,CAAC;IACvD,MAAM,MAAM,GAA2C,EAAE,CAAC;IAC1D,MAAM,eAAe,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACvF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAW,MAAA,MAAA,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,0CAAE,YAAY,mCAAI,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,IAAA,sBAAK,EAAC,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;KAC7E;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,8BAA8B,GAAG,CACtC,KAAmC,EACiB,EAAE;IACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAkBpE,MAAM,MAAM,GAA6C,EAAE,CAAC;IAC5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAW,QAAQ;aAC5B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,eAAC,OAAA,MAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,0CAAE,YAAY,mCAAI,CAAC,CAAA,EAAA,CAAC;aACjE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;KAC9C;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,KAAmC,EAAqD,EAAE;IACpH,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,MAAM,GAA6C,EAAE,CAAC;IAC5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,IAAA,sBAAK,EAAC,YAAY,CAAC,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;KACjG;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAC5B,KAAmC,EACkE,EAAE;IACvG,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,MAAM,CAAA,CAAC,CAAC;IACtF,MAAM,MAAM,GAA+D,EAAE,CAAC;IAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,eAAe,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACrF,MAAM,UAAU,GAAW,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,UAAU,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACrG,MAAM,eAAe,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,UAAU,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAClG,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;KAC9E;IACD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAC1B,KAAmC,EACnC,KAAK,GAAG,KAAK,EACkC,EAAE;IACjD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAClE,MAAM,MAAM,GAAwC,EAAE,CAAC;IACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAA,sBAAK,EAAC,WAAW,CAAC,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;KAC5F;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC7B,KAAmC,EACnC,KAAK,GAAG,KAAK,EAC0F,EAAE;IACzG,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,MAAM,CAAA,CAAC,CAAC;IAE7F,MAAM,MAAM,GAAiE,EAAE,CAAC;IAChF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,OAAO,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,eAAe,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;QACrF,MAAM,YAAY,GAAW,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,YAAY,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACzG,MAAM,eAAe,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,MAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,UAAU,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAClG,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;KAClF;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,QAAqC,EAAU,EAAE;IACpE,IAAI,CAAC,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,CAAA,EAAE;QACtB,OAAO,CAAC,CAAC;KACT;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC,CAAC","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"]}
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};\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"]}
@@ -40,7 +40,6 @@ const buildStatsForSingleHero = (rowsForHero, allRows) => {
40
40
  combatWinrateRaw: rawCombatWinrates,
41
41
  warbandStatsRaw: rawWarbandStats,
42
42
  tribeStats: tribeStats,
43
- anomalyStats: null,
44
43
  };
45
44
  return result;
46
45
  };
@@ -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 { 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"]}
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\tconst tribeStats = buildTribeStats(rowsForHero, offeredRows, averagePosition);\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};\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"]}
package/dist/models.d.ts CHANGED
@@ -41,7 +41,6 @@ export interface BgsGlobalHeroStat {
41
41
  totalStats: number;
42
42
  }[];
43
43
  readonly tribeStats: readonly BgsHeroTribeStat[];
44
- readonly anomalyStats: readonly BgsHeroAnomalyStat[];
45
44
  }
46
45
  export interface BgsHeroTribeStat {
47
46
  readonly tribe: Race;
@@ -54,40 +53,6 @@ export interface BgsHeroTribeStat {
54
53
  readonly impactAveragePosition: number;
55
54
  readonly impactAveragePositionVsMissingTribe: number;
56
55
  }
57
- export interface BgsHeroAnomalyStat {
58
- readonly anomaly: string;
59
- readonly dataPoints: number;
60
- readonly averagePosition: number;
61
- readonly impactAveragePosition: number;
62
- readonly placementDistribution: readonly {
63
- rank: number;
64
- percentage: number;
65
- }[];
66
- readonly placementDistributionRaw: readonly {
67
- rank: number;
68
- totalMatches: number;
69
- }[];
70
- readonly impactPlacementDistribution: readonly {
71
- rank: number;
72
- impact: number;
73
- }[];
74
- readonly combatWinrate: readonly {
75
- turn: number;
76
- winrate: number;
77
- }[];
78
- readonly impactCombatWinrate: readonly {
79
- turn: number;
80
- impact: number;
81
- }[];
82
- readonly warbandStats: readonly {
83
- turn: number;
84
- averageStats: number;
85
- }[];
86
- readonly impactWarbandStats: readonly {
87
- turn: number;
88
- impact: number;
89
- }[];
90
- }
91
56
  export type WithMmrAndTimePeriod<T> = T & {
92
57
  readonly mmrPercentile: MmrPercentileFilter;
93
58
  readonly timePeriod: TimePeriod;
@@ -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 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"]}
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}\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 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"]}
@@ -15,16 +15,23 @@ exports.s3 = new aws_lambda_utils_1.S3();
15
15
  const lambda = new aws_sdk_1.default.Lambda();
16
16
  exports.mmrPercentiles = [100, 50, 25, 10, 1];
17
17
  exports.default = async (event, context) => {
18
- var _a;
19
18
  if (event.catchUp) {
20
19
  await dispatchCatchUpEvents(context, +event.catchUp);
21
20
  return;
22
21
  }
23
22
  await allCards.initializeCardsDb();
24
- const dayToProcess = (_a = event.targetDate) !== null && _a !== void 0 ? _a : new Date(new Date().getTime() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
23
+ const dateTarget = event.targetDate
24
+ ? new Date(event.targetDate)
25
+ : new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
26
+ dateTarget.setHours(0);
27
+ dateTarget.setMinutes(0);
28
+ dateTarget.setSeconds(0);
29
+ dateTarget.setMilliseconds(0);
30
+ const dayToProcess = dateTarget.toISOString();
31
+ console.debug('processing date', dayToProcess, dateTarget, dateTarget.toISOString());
25
32
  for (const mmrPercentile of exports.mmrPercentiles) {
26
33
  console.log('aggregating daily cards data', dayToProcess, mmrPercentile);
27
- const hourlyData = await (0, s3_loader_1.loadHourlyDataFromS3ForDay)('card', dayToProcess, mmrPercentile);
34
+ const hourlyData = await (0, s3_loader_1.loadHourlyDataFromS3ForDay)('card', dayToProcess, null, mmrPercentile);
28
35
  const lastUpdate = hourlyData
29
36
  .map((d) => ({
30
37
  date: new Date(d.lastUpdateDate),
@@ -43,10 +50,11 @@ const dispatchCatchUpEvents = async (context, numberOfDays) => {
43
50
  const days = [];
44
51
  for (let i = 0; i < numberOfDays; i++) {
45
52
  const day = new Date(now.setDate(now.getDate() - 1));
46
- const year = day.getFullYear();
47
- const month = day.getMonth() + 1;
48
- const dayOfMonth = day.getDate();
49
- days.push(`${year}-${month}-${dayOfMonth}`);
53
+ day.setHours(0);
54
+ day.setMinutes(0);
55
+ day.setSeconds(0);
56
+ day.setMilliseconds(0);
57
+ days.push(day.toISOString());
50
58
  }
51
59
  for (const targetDate of days) {
52
60
  const newEvent = {
@@ -1 +1 @@
1
- {"version":3,"file":"_build-aggregated-stats.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-daily/cards/_build-aggregated-stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3, sleep } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { InternalBgsCardStat, InternalBgsCardStats } from '../../../internal-model';\r\nimport { MmrPercentileFilter } from '../../../models';\r\nimport { loadHourlyDataFromS3ForDay } from '../../aggregate-hourly/s3-loader';\r\nimport { persistData } from './s3-saver';\r\nimport { buildCardStats } from './stats-builder';\r\n\r\nconst allCards = new AllCardsService();\r\nexport const s3 = new S3();\r\nconst lambda = new AWS.Lambda();\r\n\r\nexport const mmrPercentiles: readonly MmrPercentileFilter[] = [100, 50, 25, 10, 1];\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tif (event.catchUp) {\r\n\t\tawait dispatchCatchUpEvents(context, +event.catchUp);\r\n\t\treturn;\r\n\t}\r\n\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\t// By default, process yesterday\r\n\tconst dayToProcess =\r\n\t\tevent.targetDate ?? new Date(new Date().getTime() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);\r\n\tfor (const mmrPercentile of mmrPercentiles) {\r\n\t\tconsole.log('aggregating daily cards data', dayToProcess, mmrPercentile);\r\n\t\tconst hourlyData: readonly InternalBgsCardStats[] = await loadHourlyDataFromS3ForDay(\r\n\t\t\t'card',\r\n\t\t\tdayToProcess,\r\n\t\t\tmmrPercentile,\r\n\t\t);\r\n\t\tconst lastUpdate = hourlyData\r\n\t\t\t.map((d) => ({\r\n\t\t\t\tdate: new Date(d.lastUpdateDate),\r\n\t\t\t\tdateStr: d.lastUpdateDate,\r\n\t\t\t\ttime: new Date(d.lastUpdateDate).getTime(),\r\n\t\t\t}))\r\n\t\t\t.sort((a, b) => b.time - a.time)[0].date;\r\n\t\tconsole.log('merging stats for day', dayToProcess, mmrPercentile);\r\n\t\tconst mergedStats: readonly InternalBgsCardStat[] = buildCardStats(hourlyData, allCards);\r\n\t\tconst totalGames = hourlyData.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tawait persistData(mergedStats, dayToProcess, totalGames, lastUpdate, mmrPercentile);\r\n\t}\r\n};\r\n\r\nconst dispatchCatchUpEvents = async (context: Context, numberOfDays: number) => {\r\n\t// Build a list of days for the last 30 days, in the format YYYY-MM-dd\r\n\tconst now = new Date();\r\n\tconst days = [];\r\n\tfor (let i = 0; i < numberOfDays; i++) {\r\n\t\tconst day = new Date(now.setDate(now.getDate() - 1));\r\n\t\tconst year = day.getFullYear();\r\n\t\tconst month = day.getMonth() + 1;\r\n\t\tconst dayOfMonth = day.getDate();\r\n\t\tdays.push(`${year}-${month}-${dayOfMonth}`);\r\n\t}\r\n\r\n\tfor (const targetDate of days) {\r\n\t\t// console.log('dispatching catch-up for date', targetDate);\r\n\t\tconst newEvent = {\r\n\t\t\ttargetDate: targetDate,\r\n\t\t};\r\n\t\tconst params = {\r\n\t\t\tFunctionName: context.functionName,\r\n\t\t\tInvocationType: 'Event',\r\n\t\t\tLogType: 'Tail',\r\n\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t};\r\n\t\t// console.log('\\tinvoking lambda', params);\r\n\t\tconst result = await lambda\r\n\t\t\t.invoke({\r\n\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\tLogType: 'Tail',\r\n\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t})\r\n\t\t\t.promise();\r\n\t\t// console.log('\\tinvocation result', result);\r\n\t\tawait sleep(50);\r\n\t}\r\n};\r\n"]}
1
+ {"version":3,"file":"_build-aggregated-stats.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-daily/cards/_build-aggregated-stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3, sleep } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { InternalBgsCardStat, InternalBgsCardStats } from '../../../internal-model';\r\nimport { MmrPercentileFilter } from '../../../models';\r\nimport { loadHourlyDataFromS3ForDay } from '../../aggregate-hourly/s3-loader';\r\nimport { persistData } from './s3-saver';\r\nimport { buildCardStats } from './stats-builder';\r\n\r\nconst allCards = new AllCardsService();\r\nexport const s3 = new S3();\r\nconst lambda = new AWS.Lambda();\r\n\r\nexport const mmrPercentiles: readonly MmrPercentileFilter[] = [100, 50, 25, 10, 1];\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tif (event.catchUp) {\r\n\t\tawait dispatchCatchUpEvents(context, +event.catchUp);\r\n\t\treturn;\r\n\t}\r\n\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\t// By default, process yesterday\r\n\tconst dateTarget: Date = event.targetDate\r\n\t\t? new Date(event.targetDate)\r\n\t\t: new Date(new Date().getTime() - 24 * 60 * 60 * 1000);\r\n\tdateTarget.setHours(0);\r\n\tdateTarget.setMinutes(0);\r\n\tdateTarget.setSeconds(0);\r\n\tdateTarget.setMilliseconds(0);\r\n\tconst dayToProcess: string = dateTarget.toISOString();\r\n\tconsole.debug('processing date', dayToProcess, dateTarget, dateTarget.toISOString());\r\n\tfor (const mmrPercentile of mmrPercentiles) {\r\n\t\tconsole.log('aggregating daily cards data', dayToProcess, mmrPercentile);\r\n\t\tconst hourlyData: readonly InternalBgsCardStats[] = await loadHourlyDataFromS3ForDay(\r\n\t\t\t'card',\r\n\t\t\tdayToProcess,\r\n\t\t\tnull,\r\n\t\t\tmmrPercentile,\r\n\t\t);\r\n\t\tconst lastUpdate = hourlyData\r\n\t\t\t.map((d) => ({\r\n\t\t\t\tdate: new Date(d.lastUpdateDate),\r\n\t\t\t\tdateStr: d.lastUpdateDate,\r\n\t\t\t\ttime: new Date(d.lastUpdateDate).getTime(),\r\n\t\t\t}))\r\n\t\t\t.sort((a, b) => b.time - a.time)[0].date;\r\n\t\tconsole.log('merging stats for day', dayToProcess, mmrPercentile);\r\n\t\tconst mergedStats: readonly InternalBgsCardStat[] = buildCardStats(hourlyData, allCards);\r\n\t\tconst totalGames = hourlyData.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tawait persistData(mergedStats, dayToProcess, totalGames, lastUpdate, mmrPercentile);\r\n\t}\r\n};\r\n\r\nconst dispatchCatchUpEvents = async (context: Context, numberOfDays: number) => {\r\n\t// Build a list of days for the last 30 days, in the format YYYY-MM-dd\r\n\tconst now = new Date();\r\n\tconst days = [];\r\n\tfor (let i = 0; i < numberOfDays; i++) {\r\n\t\tconst day = new Date(now.setDate(now.getDate() - 1));\r\n\t\tday.setHours(0);\r\n\t\tday.setMinutes(0);\r\n\t\tday.setSeconds(0);\r\n\t\tday.setMilliseconds(0);\r\n\t\t// const year = day.getFullYear();\r\n\t\t// const month = day.getMonth() + 1;\r\n\t\t// const dayOfMonth = day.getDate();\r\n\t\tdays.push(day.toISOString());\r\n\t\t// days.push(`${year}-${month}-${dayOfMonth}`);\r\n\t}\r\n\r\n\tfor (const targetDate of days) {\r\n\t\t// console.log('dispatching catch-up for date', targetDate);\r\n\t\tconst newEvent = {\r\n\t\t\ttargetDate: targetDate,\r\n\t\t};\r\n\t\tconst params = {\r\n\t\t\tFunctionName: context.functionName,\r\n\t\t\tInvocationType: 'Event',\r\n\t\t\tLogType: 'Tail',\r\n\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t};\r\n\t\t// console.log('\\tinvoking lambda', params);\r\n\t\tconst result = await lambda\r\n\t\t\t.invoke({\r\n\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\tLogType: 'Tail',\r\n\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t})\r\n\t\t\t.promise();\r\n\t\t// console.log('\\tinvocation result', result);\r\n\t\tawait sleep(50);\r\n\t}\r\n};\r\n"]}
@@ -18,7 +18,7 @@ const persistData = async (mergedStats, dayStartTime, totalGames, lastUpdate, mm
18
18
  })),
19
19
  };
20
20
  if (mmrPercentile === 100) {
21
- console.debug('Bubble Gunner daily', (_c = (_b = (_a = result.cardStats.find((s) => s.cardId === 'BG31_149')) === null || _a === void 0 ? void 0 : _a.turnStats) === null || _b === void 0 ? void 0 : _b.find((t) => t.turn === 1)) === null || _c === void 0 ? void 0 : _c.totalPlayed, mmrPercentile);
21
+ console.debug('Bubble Gunner daily', (_c = (_b = (_a = result.cardStats.find((s) => s.cardId === 'BG31_149')) === null || _a === void 0 ? void 0 : _a.turnStats) === null || _b === void 0 ? void 0 : _b.find((t) => t.turn === 1)) === null || _c === void 0 ? void 0 : _c.totalPlayed, dayStartTime, mmrPercentile);
22
22
  }
23
23
  await _build_aggregated_stats_1.s3.writeFile((0, zlib_1.gzipSync)(JSON.stringify(result)), _build_battlegrounds_hero_stats_1.STATS_BUCKET, config_1.DAILY_KEY_CARD.replace('%startDate%', dayStartTime).replace('%mmrPercentile%', `${mmrPercentile}`), 'application/json', 'gzip');
24
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"s3-saver.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-daily/cards/s3-saver.ts"],"names":[],"mappings":";;;AAAA,+BAAgC;AAGhC,0DAA+D;AAC/D,kGAA4E;AAC5E,uEAA+C;AAExC,MAAM,WAAW,GAAG,KAAK,EAC/B,WAA2C,EAC3C,YAAoB,EACpB,UAAkB,EAClB,UAAgB,EAChB,aAAkC,EACjC,EAAE;;IACH,MAAM,MAAM,GAAyB;QACpC,cAAc,EAAE,UAAU;QAC1B,cAAc,EAAE,EAAE;QAClB,UAAU,EAAE,UAAU;QACtB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrC,GAAG,IAAI;YACP,aAAa,EAAE,aAAa;YAC5B,UAAU,EAAE,IAAI;SAChB,CAAC,CAAC;KACH,CAAC;IAEF,IAAI,aAAa,KAAK,GAAG,EAAE;QAC1B,OAAO,CAAC,KAAK,CACZ,qBAAqB,EACrB,MAAA,MAAA,MAAA,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,0CAAE,SAAS,0CAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,0CAAE,WAAW,EACxG,aAAa,CACb,CAAC;KACF;IACD,MAAM,4BAAE,CAAC,SAAS,CACjB,IAAA,eAAQ,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAChC,8CAAY,EACZ,uBAAc,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC,EAClG,kBAAkB,EAClB,MAAM,CACN,CAAC;AACH,CAAC,CAAC;AAhCW,QAAA,WAAW,eAgCtB","sourcesContent":["import { gzipSync } from 'zlib';\r\nimport { InternalBgsCardStat, InternalBgsCardStats } from '../../../internal-model';\r\nimport { MmrPercentileFilter } from '../../../models';\r\nimport { DAILY_KEY_CARD } from '../../aggregate-hourly/config';\r\nimport { STATS_BUCKET } from '../../hourly/_build-battlegrounds-hero-stats';\r\nimport { s3 } from './_build-aggregated-stats';\r\n\r\nexport const persistData = async (\r\n\tmergedStats: readonly InternalBgsCardStat[],\r\n\tdayStartTime: string,\r\n\ttotalGames: number,\r\n\tlastUpdate: Date,\r\n\tmmrPercentile: MmrPercentileFilter,\r\n) => {\r\n\tconst result: InternalBgsCardStats = {\r\n\t\tlastUpdateDate: lastUpdate,\r\n\t\tmmrPercentiles: [],\r\n\t\tdataPoints: totalGames,\r\n\t\tcardStats: mergedStats.map((stat) => ({\r\n\t\t\t...stat,\r\n\t\t\tmmrPercentile: mmrPercentile,\r\n\t\t\ttimePeriod: null,\r\n\t\t})),\r\n\t};\r\n\t// console.log('persisting data', stat.dataPoints, stat.heroStats?.length);\r\n\tif (mmrPercentile === 100) {\r\n\t\tconsole.debug(\r\n\t\t\t'Bubble Gunner daily',\r\n\t\t\tresult.cardStats.find((s) => s.cardId === 'BG31_149')?.turnStats?.find((t) => t.turn === 1)?.totalPlayed,\r\n\t\t\tmmrPercentile,\r\n\t\t);\r\n\t}\r\n\tawait s3.writeFile(\r\n\t\tgzipSync(JSON.stringify(result)),\r\n\t\tSTATS_BUCKET,\r\n\t\tDAILY_KEY_CARD.replace('%startDate%', dayStartTime).replace('%mmrPercentile%', `${mmrPercentile}`),\r\n\t\t'application/json',\r\n\t\t'gzip',\r\n\t);\r\n};\r\n"]}
1
+ {"version":3,"file":"s3-saver.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-daily/cards/s3-saver.ts"],"names":[],"mappings":";;;AAAA,+BAAgC;AAGhC,0DAA+D;AAC/D,kGAA4E;AAC5E,uEAA+C;AAExC,MAAM,WAAW,GAAG,KAAK,EAC/B,WAA2C,EAC3C,YAAoB,EACpB,UAAkB,EAClB,UAAgB,EAChB,aAAkC,EACjC,EAAE;;IACH,MAAM,MAAM,GAAyB;QACpC,cAAc,EAAE,UAAU;QAC1B,cAAc,EAAE,EAAE;QAClB,UAAU,EAAE,UAAU;QACtB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrC,GAAG,IAAI;YACP,aAAa,EAAE,aAAa;YAC5B,UAAU,EAAE,IAAI;SAChB,CAAC,CAAC;KACH,CAAC;IAEF,IAAI,aAAa,KAAK,GAAG,EAAE;QAC1B,OAAO,CAAC,KAAK,CACZ,qBAAqB,EACrB,MAAA,MAAA,MAAA,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,0CAAE,SAAS,0CAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,0CAAE,WAAW,EACxG,YAAY,EACZ,aAAa,CACb,CAAC;KACF;IACD,MAAM,4BAAE,CAAC,SAAS,CACjB,IAAA,eAAQ,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAChC,8CAAY,EACZ,uBAAc,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC,EAClG,kBAAkB,EAClB,MAAM,CACN,CAAC;AACH,CAAC,CAAC;AAjCW,QAAA,WAAW,eAiCtB","sourcesContent":["import { gzipSync } from 'zlib';\r\nimport { InternalBgsCardStat, InternalBgsCardStats } from '../../../internal-model';\r\nimport { MmrPercentileFilter } from '../../../models';\r\nimport { DAILY_KEY_CARD } from '../../aggregate-hourly/config';\r\nimport { STATS_BUCKET } from '../../hourly/_build-battlegrounds-hero-stats';\r\nimport { s3 } from './_build-aggregated-stats';\r\n\r\nexport const persistData = async (\r\n\tmergedStats: readonly InternalBgsCardStat[],\r\n\tdayStartTime: string,\r\n\ttotalGames: number,\r\n\tlastUpdate: Date,\r\n\tmmrPercentile: MmrPercentileFilter,\r\n) => {\r\n\tconst result: InternalBgsCardStats = {\r\n\t\tlastUpdateDate: lastUpdate,\r\n\t\tmmrPercentiles: [],\r\n\t\tdataPoints: totalGames,\r\n\t\tcardStats: mergedStats.map((stat) => ({\r\n\t\t\t...stat,\r\n\t\t\tmmrPercentile: mmrPercentile,\r\n\t\t\ttimePeriod: null,\r\n\t\t})),\r\n\t};\r\n\t// console.log('persisting data', stat.dataPoints, stat.heroStats?.length);\r\n\tif (mmrPercentile === 100) {\r\n\t\tconsole.debug(\r\n\t\t\t'Bubble Gunner daily',\r\n\t\t\tresult.cardStats.find((s) => s.cardId === 'BG31_149')?.turnStats?.find((t) => t.turn === 1)?.totalPlayed,\r\n\t\t\tdayStartTime,\r\n\t\t\tmmrPercentile,\r\n\t\t);\r\n\t}\r\n\tawait s3.writeFile(\r\n\t\tgzipSync(JSON.stringify(result)),\r\n\t\tSTATS_BUCKET,\r\n\t\tDAILY_KEY_CARD.replace('%startDate%', dayStartTime).replace('%mmrPercentile%', `${mmrPercentile}`),\r\n\t\t'application/json',\r\n\t\t'gzip',\r\n\t);\r\n};\r\n"]}
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DAILY_KEY_CARD = exports.STAT_KEY_CARD_WITH_MMR = exports.STAT_KEY_CARD = exports.DAILY_KEY_TRINKET = exports.STAT_KEY_TRINKET = exports.DAILY_KEY_QUEST = exports.STAT_KEY_QUEST = exports.STAT_KEY_PERCENTILE = exports.DAILY_KEY_HERO = exports.STAT_KEY_HERO = void 0;
4
4
  const _build_battlegrounds_hero_stats_1 = require("../hourly/_build-battlegrounds-hero-stats");
5
- exports.STAT_KEY_HERO = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;
5
+ exports.STAT_KEY_HERO = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/%timePeriod%/%anomaly%overview-from-hourly.gz.json`;
6
6
  exports.DAILY_KEY_HERO = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;
7
- exports.STAT_KEY_PERCENTILE = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/%timePeriod%/mmr-percentiles.gz.json`;
7
+ exports.STAT_KEY_PERCENTILE = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/%timePeriod%/%anomaly%mmr-percentiles.gz.json`;
8
8
  exports.STAT_KEY_QUEST = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;
9
9
  exports.DAILY_KEY_QUEST = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;
10
10
  exports.STAT_KEY_TRINKET = `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/trinket-stats/%timePeriod%/overview-from-hourly.gz.json`;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/solo/aggregate-hourly/config.ts"],"names":[],"mappings":";;;AAAA,+FAA6E;AAEhE,QAAA,aAAa,GAAG,GAAG,kDAAgB,2EAA2E,CAAC;AAC/G,QAAA,cAAc,GAAG,GAAG,kDAAgB,2DAA2D,CAAC;AAChG,QAAA,mBAAmB,GAAG,GAAG,kDAAgB,kDAAkD,CAAC;AAC5F,QAAA,cAAc,GAAG,GAAG,kDAAgB,4EAA4E,CAAC;AACjH,QAAA,eAAe,GAAG,GAAG,kDAAgB,4DAA4D,CAAC;AAClG,QAAA,gBAAgB,GAAG,GAAG,kDAAgB,0DAA0D,CAAC;AACjG,QAAA,iBAAiB,GAAG,GAAG,kDAAgB,8DAA8D,CAAC;AACtG,QAAA,aAAa,GAAG,GAAG,kDAAgB,uDAAuD,CAAC;AAC3F,QAAA,sBAAsB,GAAG,GAAG,kDAAgB,2EAA2E,CAAC;AACxH,QAAA,cAAc,GAAG,GAAG,kDAAgB,2DAA2D,CAAC","sourcesContent":["import { STATS_KEY_PREFIX } from '../hourly/_build-battlegrounds-hero-stats';\r\n\r\nexport const STAT_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_PERCENTILE = `${STATS_KEY_PREFIX}/hero-stats/%timePeriod%/mmr-percentiles.gz.json`;\r\nexport const STAT_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_TRINKET = `${STATS_KEY_PREFIX}/trinket-stats/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_TRINKET = `${STATS_KEY_PREFIX}/trinket-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_CARD = `${STATS_KEY_PREFIX}/card-stats/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const STAT_KEY_CARD_WITH_MMR = `${STATS_KEY_PREFIX}/card-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_CARD = `${STATS_KEY_PREFIX}/card-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/solo/aggregate-hourly/config.ts"],"names":[],"mappings":";;;AAAA,+FAA6E;AAEhE,QAAA,aAAa,GAAG,GAAG,kDAAgB,oFAAoF,CAAC;AACxH,QAAA,cAAc,GAAG,GAAG,kDAAgB,2DAA2D,CAAC;AAChG,QAAA,mBAAmB,GAAG,GAAG,kDAAgB,2DAA2D,CAAC;AACrG,QAAA,cAAc,GAAG,GAAG,kDAAgB,4EAA4E,CAAC;AACjH,QAAA,eAAe,GAAG,GAAG,kDAAgB,4DAA4D,CAAC;AAClG,QAAA,gBAAgB,GAAG,GAAG,kDAAgB,0DAA0D,CAAC;AACjG,QAAA,iBAAiB,GAAG,GAAG,kDAAgB,8DAA8D,CAAC;AACtG,QAAA,aAAa,GAAG,GAAG,kDAAgB,uDAAuD,CAAC;AAC3F,QAAA,sBAAsB,GAAG,GAAG,kDAAgB,2EAA2E,CAAC;AACxH,QAAA,cAAc,GAAG,GAAG,kDAAgB,2DAA2D,CAAC","sourcesContent":["import { STATS_KEY_PREFIX } from '../hourly/_build-battlegrounds-hero-stats';\r\n\r\nexport const STAT_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/%timePeriod%/%anomaly%overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_PERCENTILE = `${STATS_KEY_PREFIX}/hero-stats/%timePeriod%/%anomaly%mmr-percentiles.gz.json`;\r\nexport const STAT_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_TRINKET = `${STATS_KEY_PREFIX}/trinket-stats/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_TRINKET = `${STATS_KEY_PREFIX}/trinket-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\nexport const STAT_KEY_CARD = `${STATS_KEY_PREFIX}/card-stats/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const STAT_KEY_CARD_WITH_MMR = `${STATS_KEY_PREFIX}/card-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const DAILY_KEY_CARD = `${STATS_KEY_PREFIX}/card-stats/mmr-%mmrPercentile%/daily/%startDate%.gz.json`;\r\n"]}
@@ -7,6 +7,8 @@ exports.s3 = void 0;
7
7
  const aws_lambda_utils_1 = require("@firestone-hs/aws-lambda-utils");
8
8
  const reference_data_1 = require("@firestone-hs/reference-data");
9
9
  const aws_sdk_1 = __importDefault(require("aws-sdk"));
10
+ const anomalies_1 = require("../../../common/anomalies");
11
+ const _build_battlegrounds_hero_stats_1 = require("../../hourly/_build-battlegrounds-hero-stats");
10
12
  const percentiles_1 = require("../percentiles");
11
13
  const s3_loader_1 = require("../s3-loader");
12
14
  const s3_saver_1 = require("./s3-saver");
@@ -14,6 +16,7 @@ const stats_merger_1 = require("./stats-merger");
14
16
  const allCards = new reference_data_1.AllCardsService();
15
17
  exports.s3 = new aws_lambda_utils_1.S3();
16
18
  const lambda = new aws_sdk_1.default.Lambda();
19
+ let cachedAllAnomalies = null;
17
20
  exports.default = async (event, context) => {
18
21
  await allCards.initializeCardsDb();
19
22
  if (!event.timePeriod) {
@@ -23,8 +26,14 @@ exports.default = async (event, context) => {
23
26
  const cleanup = (0, aws_lambda_utils_1.logBeforeTimeout)(context);
24
27
  const timePeriod = event.timePeriod;
25
28
  const mmrPercentile = event.mmrPercentile;
29
+ const anomaly = event.anomaly;
26
30
  const patchInfo = await (0, aws_lambda_utils_1.getLastBattlegroundsPatch)();
27
- const hourlyData = await (0, s3_loader_1.loadHourlyDataFromS3)('hero', timePeriod, mmrPercentile, patchInfo);
31
+ const hourlyData = await (0, s3_loader_1.loadHourlyDataFromS3)('hero', timePeriod, mmrPercentile, anomaly, patchInfo);
32
+ if (!(hourlyData === null || hourlyData === void 0 ? void 0 : hourlyData.length)) {
33
+ console.log('no data found', timePeriod, mmrPercentile, anomaly);
34
+ cleanup();
35
+ return;
36
+ }
28
37
  const lastUpdate = hourlyData
29
38
  .map((d) => ({
30
39
  date: new Date(d.lastUpdateDate),
@@ -34,35 +43,41 @@ exports.default = async (event, context) => {
34
43
  .sort((a, b) => b.time - a.time)[0].date;
35
44
  const mergedStats = (0, stats_merger_1.mergeStats)(hourlyData, mmrPercentile, allCards);
36
45
  const mmrPercentiles = (0, percentiles_1.buildMmrPercentiles)(hourlyData);
37
- const testHeroStat = mergedStats.find((stat) => stat.heroCardId === 'BG21_HERO_010');
38
- await (0, s3_saver_1.persistData)(mergedStats, mmrPercentiles, lastUpdate, timePeriod, mmrPercentile);
46
+ await (0, s3_saver_1.persistData)(mergedStats, mmrPercentiles, anomaly, lastUpdate, timePeriod, mmrPercentile);
39
47
  cleanup();
40
48
  };
41
49
  const dispatchEvents = async (context) => {
42
50
  console.log('dispatching events');
43
51
  const allTimePeriod = ['all-time', 'past-three', 'past-seven', 'last-patch'];
44
52
  const mmrPercentiles = [100, 50, 25, 10, 1];
53
+ const allAnomalies = await (0, anomalies_1.readAllAnomalies)(_build_battlegrounds_hero_stats_1.STATS_BUCKET, `${_build_battlegrounds_hero_stats_1.STATS_KEY_PREFIX}/hero-stats/anomalies`);
54
+ cachedAllAnomalies = allAnomalies;
55
+ await (0, s3_saver_1.persistAnomaliesList)(allAnomalies);
45
56
  for (const timePeriod of allTimePeriod) {
46
57
  for (const percentile of mmrPercentiles) {
47
- const newEvent = {
48
- timePeriod: timePeriod,
49
- mmrPercentile: percentile,
50
- };
51
- const params = {
52
- FunctionName: context.functionName,
53
- InvocationType: 'Event',
54
- LogType: 'Tail',
55
- Payload: JSON.stringify(newEvent),
56
- };
57
- const result = await lambda
58
- .invoke({
59
- FunctionName: context.functionName,
60
- InvocationType: 'Event',
61
- LogType: 'Tail',
62
- Payload: JSON.stringify(newEvent),
63
- })
64
- .promise();
65
- await (0, aws_lambda_utils_1.sleep)(50);
58
+ for (const anomaly of allAnomalies) {
59
+ const newEvent = {
60
+ timePeriod: timePeriod,
61
+ mmrPercentile: percentile,
62
+ anomaly: anomaly,
63
+ };
64
+ const params = {
65
+ FunctionName: context.functionName,
66
+ InvocationType: 'Event',
67
+ LogType: 'Tail',
68
+ Payload: JSON.stringify(newEvent),
69
+ };
70
+ console.log('\tinvoking lambda', params);
71
+ const result = await lambda
72
+ .invoke({
73
+ FunctionName: context.functionName,
74
+ InvocationType: 'Event',
75
+ LogType: 'Tail',
76
+ Payload: JSON.stringify(newEvent),
77
+ })
78
+ .promise();
79
+ await (0, aws_lambda_utils_1.sleep)(50);
80
+ }
66
81
  }
67
82
  }
68
83
  };
@@ -1 +1 @@
1
- {"version":3,"file":"_build-aggregated-stats.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/heroes/_build-aggregated-stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { S3, getLastBattlegroundsPatch, logBeforeTimeout, sleep } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';\r\nimport { buildMmrPercentiles } from '../percentiles';\r\nimport { loadHourlyDataFromS3 } from '../s3-loader';\r\nimport { persistData } from './s3-saver';\r\nimport { mergeStats } from './stats-merger';\r\n\r\nconst allCards = new AllCardsService();\r\nexport const s3 = new S3();\r\nconst lambda = new AWS.Lambda();\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\tif (!event.timePeriod) {\r\n\t\tawait dispatchEvents(context);\r\n\t\treturn;\r\n\t}\r\n\r\n\tconst cleanup = logBeforeTimeout(context);\r\n\tconst timePeriod: TimePeriod = event.timePeriod;\r\n\tconst mmrPercentile: MmrPercentileFilter = event.mmrPercentile;\r\n\r\n\t// console.log('aggregating data', timePeriod, mmrPercentile);\r\n\t// Build the list of files based on the timeframe, and load all of these\r\n\tconst patchInfo = await getLastBattlegroundsPatch();\r\n\tconst hourlyData: readonly BgsHeroStatsV2[] = await loadHourlyDataFromS3(\r\n\t\t'hero',\r\n\t\ttimePeriod,\r\n\t\tmmrPercentile,\r\n\t\tpatchInfo,\r\n\t);\r\n\t// console.log('hourlyData', hourlyData.length);\r\n\tconst lastUpdate = hourlyData\r\n\t\t.map((d) => ({\r\n\t\t\tdate: new Date(d.lastUpdateDate),\r\n\t\t\tdateStr: d.lastUpdateDate,\r\n\t\t\ttime: new Date(d.lastUpdateDate).getTime(),\r\n\t\t}))\r\n\t\t.sort((a, b) => b.time - a.time)[0].date;\r\n\t// console.log('loaded hourly data', lastUpdate);\r\n\r\n\t// Here it's ok that the MMR corresponding to each MMR percentile is not the same across all the hourly data chunks\r\n\t// as the actual MMR evolves over time\r\n\t// The only issue is that the samples might be too small for the MMR percentiles to be really representative in\r\n\t// each group\r\n\t// Empirically, it looks like there isn't much variation on a hour-by-hour basis, so it should be ok\r\n\t// A possible solution, if this becomes an issue, is to compute the MMR percentiles on the full last day of data\r\n\t// when building the hourly data\r\n\tconst mergedStats: readonly BgsGlobalHeroStat[] = mergeStats(hourlyData, mmrPercentile, allCards);\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(hourlyData);\r\n\t// console.log(\r\n\t// \t'mergedStats',\r\n\t// \tmergedStats?.map((s) => s.dataPoints).reduce((a, b) => a + b, 0),\r\n\t// \tmergedStats?.length,\r\n\t// );\r\n\tconst testHeroStat = mergedStats.find((stat) => stat.heroCardId === 'BG21_HERO_010');\r\n\t// console.log('testHeroStat', testHeroStat, testHeroStat?.combatWinrate);\r\n\r\n\tawait persistData(mergedStats, mmrPercentiles, lastUpdate, timePeriod, mmrPercentile);\r\n\tcleanup();\r\n};\r\n\r\nconst dispatchEvents = async (context: Context) => {\r\n\tconsole.log('dispatching events');\r\n\tconst allTimePeriod: readonly TimePeriod[] = ['all-time', 'past-three', 'past-seven', 'last-patch'];\r\n\tconst mmrPercentiles: readonly MmrPercentileFilter[] = [100, 50, 25, 10, 1];\r\n\tfor (const timePeriod of allTimePeriod) {\r\n\t\tfor (const percentile of mmrPercentiles) {\r\n\t\t\tconst newEvent = {\r\n\t\t\t\ttimePeriod: timePeriod,\r\n\t\t\t\tmmrPercentile: percentile,\r\n\t\t\t};\r\n\t\t\tconst params = {\r\n\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\tLogType: 'Tail',\r\n\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t};\r\n\t\t\t// console.log('\\tinvoking lambda', params);\r\n\t\t\tconst result = await lambda\r\n\t\t\t\t.invoke({\r\n\t\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\t\tLogType: 'Tail',\r\n\t\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t\t})\r\n\t\t\t\t.promise();\r\n\t\t\t// console.log('\\tinvocation result', result);\r\n\t\t\tawait sleep(50);\r\n\t\t}\r\n\t}\r\n};\r\n"]}
1
+ {"version":3,"file":"_build-aggregated-stats.js","sourceRoot":"","sources":["../../../../src/solo/aggregate-hourly/heroes/_build-aggregated-stats.ts"],"names":[],"mappings":";;;;;;AAAA,qEAAwG;AACxG,iEAA+D;AAE/D,sDAA0B;AAC1B,yDAA6D;AAE7D,kGAA8F;AAC9F,gDAAqD;AACrD,4CAAoD;AACpD,yCAA+D;AAC/D,iDAA4C;AAE5C,MAAM,QAAQ,GAAG,IAAI,gCAAe,EAAE,CAAC;AAC1B,QAAA,EAAE,GAAG,IAAI,qBAAE,EAAE,CAAC;AAC3B,MAAM,MAAM,GAAG,IAAI,iBAAG,CAAC,MAAM,EAAE,CAAC;AAEhC,IAAI,kBAAkB,GAA+B,IAAI,CAAC;AAE1D,kBAAe,KAAK,EAAE,KAAK,EAAE,OAAgB,EAAgB,EAAE;IAC9D,MAAM,QAAQ,CAAC,iBAAiB,EAAE,CAAC;IAEnC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QACtB,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO;KACP;IAED,MAAM,OAAO,GAAG,IAAA,mCAAgB,EAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAe,KAAK,CAAC,UAAU,CAAC;IAChD,MAAM,aAAa,GAAwB,KAAK,CAAC,aAAa,CAAC;IAC/D,MAAM,OAAO,GAAkB,KAAK,CAAC,OAAO,CAAC;IAI7C,MAAM,SAAS,GAAG,MAAM,IAAA,4CAAyB,GAAE,CAAC;IACpD,MAAM,UAAU,GAA8B,MAAM,IAAA,gCAAoB,EACvE,MAAM,EACN,UAAU,EACV,aAAa,EACb,OAAO,EACP,SAAS,CACT,CAAC;IACF,IAAI,CAAC,CAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,MAAM,CAAA,EAAE;QACxB,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO,EAAE,CAAC;QACV,OAAO;KACP;IACD,MAAM,UAAU,GAAG,UAAU;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACZ,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;QAChC,OAAO,EAAE,CAAC,CAAC,cAAc;QACzB,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE;KAC1C,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAU1C,MAAM,WAAW,GAAiC,IAAA,yBAAU,EAAC,UAAU,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAClG,MAAM,cAAc,GAA6B,IAAA,iCAAmB,EAAC,UAAU,CAAC,CAAC;IASjF,MAAM,IAAA,sBAAW,EAAC,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IAC/F,OAAO,EAAE,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,KAAK,EAAE,OAAgB,EAAE,EAAE;IACjD,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClC,MAAM,aAAa,GAA0B,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACpG,MAAM,cAAc,GAAmC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,YAAY,GAA+B,MAAM,IAAA,4BAAgB,EACtE,8CAAY,EACZ,GAAG,kDAAgB,uBAAuB,CAC1C,CAAC;IACF,kBAAkB,GAAG,YAAY,CAAC;IAClC,MAAM,IAAA,+BAAoB,EAAC,YAAY,CAAC,CAAC;IACzC,KAAK,MAAM,UAAU,IAAI,aAAa,EAAE;QACvC,KAAK,MAAM,UAAU,IAAI,cAAc,EAAE;YACxC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE;gBACnC,MAAM,QAAQ,GAAG;oBAChB,UAAU,EAAE,UAAU;oBACtB,aAAa,EAAE,UAAU;oBACzB,OAAO,EAAE,OAAO;iBAChB,CAAC;gBACF,MAAM,MAAM,GAAG;oBACd,YAAY,EAAE,OAAO,CAAC,YAAY;oBAClC,cAAc,EAAE,OAAO;oBACvB,OAAO,EAAE,MAAM;oBACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;iBACjC,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;gBACzC,MAAM,MAAM,GAAG,MAAM,MAAM;qBACzB,MAAM,CAAC;oBACP,YAAY,EAAE,OAAO,CAAC,YAAY;oBAClC,cAAc,EAAE,OAAO;oBACvB,OAAO,EAAE,MAAM;oBACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;iBACjC,CAAC;qBACD,OAAO,EAAE,CAAC;gBAEZ,MAAM,IAAA,wBAAK,EAAC,EAAE,CAAC,CAAC;aAChB;SACD;KACD;AACF,CAAC,CAAC","sourcesContent":["import { S3, getLastBattlegroundsPatch, logBeforeTimeout, sleep } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { readAllAnomalies } from '../../../common/anomalies';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';\r\nimport { STATS_BUCKET, STATS_KEY_PREFIX } from '../../hourly/_build-battlegrounds-hero-stats';\r\nimport { buildMmrPercentiles } from '../percentiles';\r\nimport { loadHourlyDataFromS3 } from '../s3-loader';\r\nimport { persistAnomaliesList, persistData } from './s3-saver';\r\nimport { mergeStats } from './stats-merger';\r\n\r\nconst allCards = new AllCardsService();\r\nexport const s3 = new S3();\r\nconst lambda = new AWS.Lambda();\r\n\r\nlet cachedAllAnomalies: readonly (string | null)[] = null;\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\tif (!event.timePeriod) {\r\n\t\tawait dispatchEvents(context);\r\n\t\treturn;\r\n\t}\r\n\r\n\tconst cleanup = logBeforeTimeout(context);\r\n\tconst timePeriod: TimePeriod = event.timePeriod;\r\n\tconst mmrPercentile: MmrPercentileFilter = event.mmrPercentile;\r\n\tconst anomaly: string | null = event.anomaly;\r\n\r\n\t// console.log('aggregating data', timePeriod, mmrPercentile);\r\n\t// Build the list of files based on the timeframe, and load all of these\r\n\tconst patchInfo = await getLastBattlegroundsPatch();\r\n\tconst hourlyData: readonly BgsHeroStatsV2[] = await loadHourlyDataFromS3(\r\n\t\t'hero',\r\n\t\ttimePeriod,\r\n\t\tmmrPercentile,\r\n\t\tanomaly,\r\n\t\tpatchInfo,\r\n\t);\r\n\tif (!hourlyData?.length) {\r\n\t\tconsole.log('no data found', timePeriod, mmrPercentile, anomaly);\r\n\t\tcleanup();\r\n\t\treturn;\r\n\t}\r\n\tconst lastUpdate = hourlyData\r\n\t\t.map((d) => ({\r\n\t\t\tdate: new Date(d.lastUpdateDate),\r\n\t\t\tdateStr: d.lastUpdateDate,\r\n\t\t\ttime: new Date(d.lastUpdateDate).getTime(),\r\n\t\t}))\r\n\t\t.sort((a, b) => b.time - a.time)[0].date;\r\n\t// console.log('loaded hourly data', lastUpdate);\r\n\r\n\t// Here it's ok that the MMR corresponding to each MMR percentile is not the same across all the hourly data chunks\r\n\t// as the actual MMR evolves over time\r\n\t// The only issue is that the samples might be too small for the MMR percentiles to be really representative in\r\n\t// each group\r\n\t// Empirically, it looks like there isn't much variation on a hour-by-hour basis, so it should be ok\r\n\t// A possible solution, if this becomes an issue, is to compute the MMR percentiles on the full last day of data\r\n\t// when building the hourly data\r\n\tconst mergedStats: readonly BgsGlobalHeroStat[] = mergeStats(hourlyData, mmrPercentile, allCards);\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(hourlyData);\r\n\t// console.log(\r\n\t// \t'mergedStats',\r\n\t// \tmergedStats?.map((s) => s.dataPoints).reduce((a, b) => a + b, 0),\r\n\t// \tmergedStats?.length,\r\n\t// );\r\n\t// const testHeroStat = mergedStats.find((stat) => stat.heroCardId === 'BG21_HERO_010');\r\n\t// console.log('testHeroStat', testHeroStat, testHeroStat?.combatWinrate);\r\n\r\n\tawait persistData(mergedStats, mmrPercentiles, anomaly, lastUpdate, timePeriod, mmrPercentile);\r\n\tcleanup();\r\n};\r\n\r\nconst dispatchEvents = async (context: Context) => {\r\n\tconsole.log('dispatching events');\r\n\tconst allTimePeriod: readonly TimePeriod[] = ['all-time', 'past-three', 'past-seven', 'last-patch'];\r\n\tconst mmrPercentiles: readonly MmrPercentileFilter[] = [100, 50, 25, 10, 1];\r\n\tconst allAnomalies: readonly (string | null)[] = await readAllAnomalies(\r\n\t\tSTATS_BUCKET,\r\n\t\t`${STATS_KEY_PREFIX}/hero-stats/anomalies`,\r\n\t);\r\n\tcachedAllAnomalies = allAnomalies;\r\n\tawait persistAnomaliesList(allAnomalies);\r\n\tfor (const timePeriod of allTimePeriod) {\r\n\t\tfor (const percentile of mmrPercentiles) {\r\n\t\t\tfor (const anomaly of allAnomalies) {\r\n\t\t\t\tconst newEvent = {\r\n\t\t\t\t\ttimePeriod: timePeriod,\r\n\t\t\t\t\tmmrPercentile: percentile,\r\n\t\t\t\t\tanomaly: anomaly,\r\n\t\t\t\t};\r\n\t\t\t\tconst params = {\r\n\t\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\t\tLogType: 'Tail',\r\n\t\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t\t};\r\n\t\t\t\tconsole.log('\\tinvoking lambda', params);\r\n\t\t\t\tconst result = await lambda\r\n\t\t\t\t\t.invoke({\r\n\t\t\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\t\t\tLogType: 'Tail',\r\n\t\t\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t\t\t})\r\n\t\t\t\t\t.promise();\r\n\t\t\t\t// console.log('\\tinvocation result', result);\r\n\t\t\t\tawait sleep(50);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};\r\n"]}
@@ -1,2 +1,3 @@
1
1
  import { BgsGlobalHeroStat, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';
2
- export declare const persistData: (mergedStats: readonly BgsGlobalHeroStat[], mmrPercentiles: readonly MmrPercentile[], lastUpdate: Date, timePeriod: TimePeriod, mmrPercentile: MmrPercentileFilter) => Promise<void>;
2
+ export declare const persistAnomaliesList: (anomalies: readonly string[]) => Promise<void>;
3
+ export declare const persistData: (mergedStats: readonly BgsGlobalHeroStat[], mmrPercentiles: readonly MmrPercentile[], anomaly: string | null, lastUpdate: Date, timePeriod: TimePeriod, mmrPercentile: MmrPercentileFilter) => Promise<void>;