From 96015d402bd6b6538ef40bb7b913a48141237b2a Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 20 Jun 2021 12:57:32 -0400 Subject: [PATCH] Make reputation take into account self downvotes --- src/routes/getSkipSegments.ts | 4 ++-- src/utils/reputation.ts | 15 +++++++++++++-- test/cases/getUserInfo.ts | 2 +- test/cases/reputation.ts | 18 +++++++++++++++++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/routes/getSkipSegments.ts b/src/routes/getSkipSegments.ts index 0af3470..3889e32 100644 --- a/src/routes/getSkipSegments.ts +++ b/src/routes/getSkipSegments.ts @@ -173,8 +173,8 @@ function getWeightedRandomChoice(choices: T[], amountOf //The 3 makes -2 the minimum votes before being ignored completely //this can be changed if this system increases in popularity. - const weight = Math.exp(choice.votes * Math.min(1, choice.reputation + 1) + 3 + boost); - totalWeight += weight; + const weight = Math.exp(choice.votes * Math.max(1, choice.reputation + 1) + 3 + boost); + totalWeight += Math.max(weight, 0); return {...choice, weight}; }); diff --git a/src/utils/reputation.ts b/src/utils/reputation.ts index d40857c..3fe42c1 100644 --- a/src/utils/reputation.ts +++ b/src/utils/reputation.ts @@ -6,6 +6,7 @@ import { reputationKey } from "./redisKeys"; interface ReputationDBResult { totalSubmissions: number, downvotedSubmissions: number, + nonSelfDownvotedSubmissions: number, upvotedSum: number, lockedSum: number, oldUpvotedSubmissions: number @@ -17,10 +18,15 @@ export async function getReputation(userID: UserID): Promise { const fetchFromDB = () => db.prepare("get", `SELECT COUNT(*) AS "totalSubmissions", SUM(CASE WHEN "votes" < 0 THEN 1 ELSE 0 END) AS "downvotedSubmissions", + SUM(CASE WHEN "votes" < 0 AND "videoID" NOT IN + (SELECT b."videoID" FROM "sponsorTimes" as b + WHERE b."userID" = ? + AND b."votes" > 0 AND b."category" = "a"."category" AND b."videoID" = "a"."videoID" LIMIT 1) + THEN 1 ELSE 0 END) AS "nonSelfDownvotedSubmissions", SUM(CASE WHEN "votes" > 0 AND "timeSubmitted" > 1596240000000 THEN "votes" ELSE 0 END) AS "upvotedSum", SUM(locked) AS "lockedSum", SUM(CASE WHEN "timeSubmitted" < ? AND "timeSubmitted" > 1596240000000 AND "votes" > 0 THEN 1 ELSE 0 END) AS "oldUpvotedSubmissions" - FROM "sponsorTimes" WHERE "userID" = ?`, [pastDate, userID]) as Promise; + FROM "sponsorTimes" as "a" WHERE "userID" = ?`, [userID, pastDate, userID]) as Promise; const result = await QueryCacher.get(fetchFromDB, reputationKey(userID)); @@ -31,7 +37,12 @@ export async function getReputation(userID: UserID): Promise { const downvoteRatio = result.downvotedSubmissions / result.totalSubmissions; if (downvoteRatio > 0.3) { - return convertRange(downvoteRatio, 0.3, 1, -0.5, -1.5); + return convertRange(Math.min(downvoteRatio, 0.7), 0.3, 0.7, -0.5, -2.5); + } + + const nonSelfDownvoteRatio = result.nonSelfDownvotedSubmissions / result.totalSubmissions; + if (nonSelfDownvoteRatio > 0.05) { + return convertRange(Math.min(nonSelfDownvoteRatio, 0.4), 0.05, 0.4, -0.5, -2.5); } if (result.oldUpvotedSubmissions < 3 || result.upvotedSum < 5) { diff --git a/test/cases/getUserInfo.ts b/test/cases/getUserInfo.ts index 06c53ec..c20a0ea 100644 --- a/test/cases/getUserInfo.ts +++ b/test/cases/getUserInfo.ts @@ -60,7 +60,7 @@ describe('getUserInfo', () => { done('Returned incorrect segmentCount "' + data.segmentCount + '"'); } else if (data.ignoredSegmentCount !== 2) { done('Returned incorrect ignoredSegmentCount "' + data.ignoredSegmentCount + '"'); - } else if (Math.abs(data.reputation - -0.928) > 0.001) { + } else if (data.reputation !== -2) { done('Returned incorrect reputation "' + data.reputation + '"'); } else if (data.lastSegmentID !== "uuid000005") { done('Returned incorrect last segment "' + data.lastSegmentID + '"'); diff --git a/test/cases/reputation.ts b/test/cases/reputation.ts index 7fe9226..77b4179 100644 --- a/test/cases/reputation.ts +++ b/test/cases/reputation.ts @@ -6,6 +6,7 @@ import { getReputation } from '../../src/utils/reputation'; const userIDLowSubmissions = "reputation-lowsubmissions" as UserID; const userIDHighDownvotes = "reputation-highdownvotes" as UserID; +const userIDHighNonSelfDownvotes = "reputation-highnonselfdownvotes" as UserID; const userIDNewSubmissions = "reputation-newsubmissions" as UserID; const userIDLowSum = "reputation-lowsum" as UserID; const userIDHighRepBeforeManualVote = "reputation-oldhighrep" as UserID; @@ -29,6 +30,17 @@ describe('reputation', () => { await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, -1, 0, 'reputation-1-uuid-5', '${getHash(userIDHighDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-uuid-6', '${getHash(userIDHighDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-uuid-7', '${getHash(userIDHighDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + + // Each downvote is on a different video (ie. they didn't resubmit to fix their downvote) + await db.prepare("run", startOfQuery + `('${videoID}A', 1, 11, 2, 0, 'reputation-1-1-uuid-0', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + // Different category, same video + await db.prepare("run", startOfQuery + `('${videoID}A', 1, 11, -2, 0, 'reputation-1-1-uuid-1', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'intro', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-1-uuid-2', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-1-uuid-3', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-1-uuid-4', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, -1, 0, 'reputation-1-1-uuid-5', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-1-uuid-6', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); + await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 0, 0, 'reputation-1-1-uuid-7', '${getHash(userIDHighNonSelfDownvotes)}', 1606240000000, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 2, 0, 'reputation-2-uuid-0', '${getHash(userIDNewSubmissions)}', ${Date.now()}, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); await db.prepare("run", startOfQuery + `('${videoID}', 1, 11, 2, 0, 'reputation-2-uuid-1', '${getHash(userIDNewSubmissions)}', ${Date.now()}, 50, 'sponsor', 'YouTube', 100, 0, 0, '${getHash(videoID, 1)}')`); @@ -81,7 +93,11 @@ describe('reputation', () => { }); it("user with high downvote ratio", async () => { - assert.strictEqual(await getReputation(getHash(userIDHighDownvotes)), -0.9642857142857144); + assert.strictEqual(await getReputation(getHash(userIDHighDownvotes)), -2.125); + }); + + it("user with high non self downvote ratio", async () => { + assert.strictEqual(await getReputation(getHash(userIDHighNonSelfDownvotes)), -1.6428571428571428); }); it("user with mostly new submissions", async () => {