diff --git a/server/package.json b/server/package.json index e9ad43632..1acac5c0e 100644 --- a/server/package.json +++ b/server/package.json @@ -82,7 +82,7 @@ "rimraf": "3.0.2", "safe-json-stringify": "1.2.0", "seq-logging": "^1.1.1", - "tachi-common": "0.4.12", + "tachi-common": "0.4.14", "ts-node": "10.0.0", "tsconfig-paths": "3.10.1", "typescript": "4.3.4", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index 8cacb6b16..5f73cea47 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -63,7 +63,7 @@ specifiers: safe-json-stringify: 1.2.0 seq-logging: ^1.1.1 supertest: 6.1.3 - tachi-common: 0.4.12 + tachi-common: 0.4.14 tap: 15.0.9 ts-node: 10.0.0 tsconfig-paths: 3.10.1 @@ -107,7 +107,7 @@ dependencies: rimraf: 3.0.2 safe-json-stringify: 1.2.0 seq-logging: 1.1.1 - tachi-common: 0.4.12 + tachi-common: 0.4.14 ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b tsconfig-paths: 3.10.1 typescript: 4.3.4 @@ -5386,8 +5386,8 @@ packages: strip-ansi: 6.0.0 dev: true - /tachi-common/0.4.12: - resolution: {integrity: sha512-PsmVP5oJNnCvx3Omc6s6TVSheHmmiG/MJJAtUtcdeG8fwx38ulevVBiB2lSLTUldAh+HMtRvg5m0Qf/ts1QlwQ==} + /tachi-common/0.4.14: + resolution: {integrity: sha512-+D5ljllIWQNRohyENL/IhK+E/st7lP3kj5EydlxBCDla9eFGLFerwp7XxWtCj4tjvH3Mc0zhwuAU4P5y6gd9Cg==} dependencies: monk: 7.3.4 transitivePeerDependencies: diff --git a/server/src/lib/score-import/framework/user-game-stats/rating.test.ts b/server/src/lib/score-import/framework/user-game-stats/rating.test.ts index 7de6183db..70b7a63ac 100644 --- a/server/src/lib/score-import/framework/user-game-stats/rating.test.ts +++ b/server/src/lib/score-import/framework/user-game-stats/rating.test.ts @@ -11,11 +11,15 @@ t.test("#CalculateRatings", (t) => { t.test("Should return BPI for IIDX", async (t) => { const res = await CalculateRatings("iidx", "SP", 1, logger); - t.strictSame(res, { BPI: 0, ktLampRating: 0 }, "Should return BPI as a custom key."); + t.strictSame(res, { BPI: null, ktLampRating: null }, "Should return BPI as a custom key."); const resDP = await CalculateRatings("iidx", "DP", 1, logger); - t.strictSame(resDP, { BPI: 0, ktLampRating: 0 }, "Should return BPI as a custom key."); + t.strictSame( + resDP, + { BPI: null, ktLampRating: null }, + "Should return BPI as a custom key." + ); t.end(); }); @@ -23,7 +27,7 @@ t.test("#CalculateRatings", (t) => { t.test("Should return VF6 for SDVX", async (t) => { const res = await CalculateRatings("sdvx", "Single", 1, logger); - t.strictSame(res, { VF6: 0 }, "Should return VF6 keys."); + t.strictSame(res, { VF6: null }, "Should return VF6 keys."); t.end(); }); @@ -31,7 +35,7 @@ t.test("#CalculateRatings", (t) => { t.test("Should return VF6 for USC:Keyboard", async (t) => { const res = await CalculateRatings("usc", "Keyboard", 1, logger); - t.strictSame(res, { VF6: 0 }, "Should return VF6 keys."); + t.strictSame(res, { VF6: null }, "Should return VF6 keys."); t.end(); }); @@ -39,7 +43,7 @@ t.test("#CalculateRatings", (t) => { t.test("Should return VF6 for USC:Controller", async (t) => { const res = await CalculateRatings("usc", "Controller", 1, logger); - t.strictSame(res, { VF6: 0 }, "Should return VF6 keys."); + t.strictSame(res, { VF6: null }, "Should return VF6 keys."); t.end(); }); @@ -47,11 +51,11 @@ t.test("#CalculateRatings", (t) => { t.test("Should return MFCP for DDR", async (t) => { const res = await CalculateRatings("ddr", "SP", 1, logger); - t.strictSame(res, { MFCP: 0, ktRating: 0 }, "Should return MFCP keys."); + t.strictSame(res, { MFCP: null, ktRating: null }, "Should return MFCP keys."); const resDP = await CalculateRatings("ddr", "DP", 1, logger); - t.strictSame(resDP, { MFCP: 0, ktRating: 0 }, "Should return MFCP keys."); + t.strictSame(resDP, { MFCP: null, ktRating: null }, "Should return MFCP keys."); t.end(); }); @@ -59,11 +63,11 @@ t.test("#CalculateRatings", (t) => { t.test("Should return skill for Gitadora", async (t) => { const res = await CalculateRatings("gitadora", "Dora", 1, logger); - t.strictSame(res, { skill: 0 }, "Should return skill keys."); + t.strictSame(res, { skill: null }, "Should return skill keys."); const resDP = await CalculateRatings("gitadora", "Gita", 1, logger); - t.strictSame(resDP, { skill: 0 }, "Should return skill keys."); + t.strictSame(resDP, { skill: null }, "Should return skill keys."); t.end(); }); diff --git a/server/src/lib/score-import/framework/user-game-stats/rating.ts b/server/src/lib/score-import/framework/user-game-stats/rating.ts index e9a442dbf..6b58badcb 100644 --- a/server/src/lib/score-import/framework/user-game-stats/rating.ts +++ b/server/src/lib/score-import/framework/user-game-stats/rating.ts @@ -19,15 +19,29 @@ function LazySumAll(key: CustomCalcNames) { playtype: playtype, userID: userID, isPrimary: true, - [`calculatedData.${key}`]: { $gt: 0 }, + [`calculatedData.${key}`]: { $type: "number" }, }); + if (sc.length === 0) { + return null; + } + const result = sc.reduce((a, b) => a + b.calculatedData[key]!, 0); return result; }; } +/** + * Curries a function that returns the sum of N best ratings on `key`. + * + * @param key - What rating value to sum. + * @param n - The amount of rating values to pull. + * @param returnMean - Optionally, if true, return the sum of these values divided by N. + * + * @returns - Number if the user has scores with that rating algorithm, null if they have + * no scores with this rating algorithm that are non-null. + */ function LazyCalcN(key: CustomCalcNames, n: integer, returnMean?: boolean) { return async (game: Game, playtype: Playtypes[Game], userID: integer) => { const sc = await db["personal-bests"].find( @@ -36,7 +50,7 @@ function LazyCalcN(key: CustomCalcNames, n: integer, returnMean?: boolean) { playtype: playtype, userID: userID, isPrimary: true, - [`calculatedData.${key}`]: { $gt: 0 }, + [`calculatedData.${key}`]: { $type: "number" }, }, { limit: n, @@ -44,6 +58,10 @@ function LazyCalcN(key: CustomCalcNames, n: integer, returnMean?: boolean) { } ); + if (sc.length === 0) { + return null; + } + let result = sc.reduce((a, b) => a + b.calculatedData[key]!, 0); if (returnMean) { @@ -210,6 +228,10 @@ async function CalculateWACCARate( } ); + if (best15Hot.length + best35Cold.length === 0) { + return null; + } + return ( best15Hot.reduce((a, r) => a + r.calculatedData.rate!, 0) + best35Cold.reduce((a, r) => a + r.calculatedData.rate!, 0) @@ -236,6 +258,10 @@ async function CalculateGitadoraSkill( GetBestRatingOnSongs(coldSongIDs, userID, game, playtype, "skill"), ]); + if (bestHotScores.length + bestScores.length === 0) { + return null; + } + let skill = 0; skill += bestHotScores.reduce((a, r) => a + r.calculatedData.skill!, 0); skill += bestScores.reduce((a, r) => a + r.calculatedData.skill!, 0); diff --git a/server/src/lib/score-import/framework/user-game-stats/update-ugs.test.ts b/server/src/lib/score-import/framework/user-game-stats/update-ugs.test.ts index 07cd3f271..c7cce3024 100644 --- a/server/src/lib/score-import/framework/user-game-stats/update-ugs.test.ts +++ b/server/src/lib/score-import/framework/user-game-stats/update-ugs.test.ts @@ -12,6 +12,10 @@ const logger = CreateLogCtx(__filename); // more of an integration test t.test("#UpdateUsersGamePlaytypeStats", (t) => { t.beforeEach(ResetDBState); + t.beforeEach(async () => { + delete TestingIIDXSPScorePB._id; + await db["personal-bests"].insert(TestingIIDXSPScorePB); + }); t.test( "Should create new UserGameStats and UserGameSettings if the user has none", diff --git a/server/src/server/router/ir/usc/_playtype/usc.ts b/server/src/server/router/ir/usc/_playtype/usc.ts index 3d0eafb85..bc556b7a2 100644 --- a/server/src/server/router/ir/usc/_playtype/usc.ts +++ b/server/src/server/router/ir/usc/_playtype/usc.ts @@ -222,6 +222,9 @@ export function ConvertUSCChart(uscChartDoc: USCClientChart, playtype: Playtypes data: { hashSHA1: uscChartDoc.chartHash, isOfficial: false, + bpm: uscChartDoc.bpm, + effector: uscChartDoc.effector, + illustrator: uscChartDoc.illustrator, }, }; diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts index d817c03a3..5e12bfeb5 100644 --- a/server/src/test-utils/test-data.ts +++ b/server/src/test-utils/test-data.ts @@ -502,6 +502,9 @@ export const uscChart: ChartDocument<"usc:Controller" | "usc:Keyboard"> = { data: { hashSHA1: "USC_CHART_HASH", isOfficial: false, + bpm: "128", + effector: "foo", + illustrator: "bar", }, tierlistInfo: {}, isPrimary: true,