Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ranking System v2 #1186

Merged
merged 17 commits into from May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
110 changes: 37 additions & 73 deletions src/calculateRank.js
@@ -1,93 +1,57 @@
// https://stackoverflow.com/a/5263759/10629172
function normalcdf(mean, sigma, to) {
var z = (to - mean) / Math.sqrt(2 * sigma * sigma);
var t = 1 / (1 + 0.3275911 * Math.abs(z));
var a1 = 0.254829592;
var a2 = -0.284496736;
var a3 = 1.421413741;
var a4 = -1.453152027;
var a5 = 1.061405429;
var erf =
1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-z * z);
var sign = 1;
if (z < 0) {
sign = -1;
}
return (1 / 2) * (1 + sign * erf);
function expsf(x, lambda=1.) {
return 2 ** (-lambda * x);
}

function calculateRank({
totalRepos,
totalRepos, // unused
totalCommits,
contributions,
contributions, // unused
followers,
prs,
issues,
stargazers,
}) {
const COMMITS_OFFSET = 1.65;
const CONTRIBS_OFFSET = 1.65;
const ISSUES_OFFSET = 1;
const STARS_OFFSET = 0.75;
const PRS_OFFSET = 0.5;
const FOLLOWERS_OFFSET = 0.45;
const REPO_OFFSET = 1;

const ALL_OFFSETS =
CONTRIBS_OFFSET +
ISSUES_OFFSET +
STARS_OFFSET +
PRS_OFFSET +
FOLLOWERS_OFFSET +
REPO_OFFSET;

const RANK_S_VALUE = 1;
const RANK_DOUBLE_A_VALUE = 25;
const RANK_A2_VALUE = 45;
const RANK_A3_VALUE = 60;
const RANK_B_VALUE = 100;

const TOTAL_VALUES =
RANK_S_VALUE + RANK_A2_VALUE + RANK_A3_VALUE + RANK_B_VALUE;

// prettier-ignore
const score = (
totalCommits * COMMITS_OFFSET +
contributions * CONTRIBS_OFFSET +
issues * ISSUES_OFFSET +
stargazers * STARS_OFFSET +
prs * PRS_OFFSET +
followers * FOLLOWERS_OFFSET +
totalRepos * REPO_OFFSET
) / 100;

const normalizedScore = normalcdf(score, TOTAL_VALUES, ALL_OFFSETS) * 100;
const COMMITS_MEAN = 500, COMMITS_WEIGHT = 0.25;
const FOLLOWERS_MEAN = 50, FOLLOWERS_WEIGHT = 0.25;
const PRS_ISSUES_MEAN = 25, PRS_ISSUES_WEIGHT = 0.25;
const STARS_MEAN = 100, STARS_WEIGHT = 1.0;

const TOTAL_WEIGHT = (
COMMITS_WEIGHT +
FOLLOWERS_WEIGHT +
PRS_ISSUES_WEIGHT +
STARS_WEIGHT
);

const rank = (
COMMITS_WEIGHT * expsf(totalCommits, 1 / COMMITS_MEAN) +
FOLLOWERS_WEIGHT * expsf(followers, 1 / FOLLOWERS_MEAN) +
PRS_ISSUES_WEIGHT * expsf(prs + issues, 1 / PRS_ISSUES_MEAN) +
STARS_WEIGHT * expsf(stargazers, 1 / STARS_MEAN)
) / TOTAL_WEIGHT;

const RANK_S_PLUS = 0.025;
const RANK_S = 0.1;
const RANK_A_PLUS = 0.25;
const RANK_A = 0.50;
const RANK_B_PLUS = 0.75;

let level = "";

if (normalizedScore < RANK_S_VALUE) {
if (rank <= RANK_S_PLUS)
level = "S+";
}
if (
normalizedScore >= RANK_S_VALUE &&
normalizedScore < RANK_DOUBLE_A_VALUE
) {
else if (rank <= RANK_S)
level = "S";
}
if (
normalizedScore >= RANK_DOUBLE_A_VALUE &&
normalizedScore < RANK_A2_VALUE
) {
level = "A++";
}
if (normalizedScore >= RANK_A2_VALUE && normalizedScore < RANK_A3_VALUE) {
else if (rank <= RANK_A_PLUS)
level = "A+";
}
if (normalizedScore >= RANK_A3_VALUE && normalizedScore < RANK_B_VALUE) {
else if (rank <= RANK_A)
level = "A";
else if (rank <= RANK_B_PLUS)
level = "B+";
}
else
level = "B";

return { level, score: normalizedScore };
return {level, score: rank * 100};
}

module.exports = calculateRank;
46 changes: 37 additions & 9 deletions tests/calculateRank.test.js
Expand Up @@ -2,17 +2,45 @@ require("@testing-library/jest-dom");
const calculateRank = require("../src/calculateRank");

describe("Test calculateRank", () => {
it("should calculate rank correctly", () => {
it("new user gets B rank", () => {
expect(
calculateRank({
totalCommits: 100,
totalRepos: 5,
followers: 100,
contributions: 61,
stargazers: 400,
prs: 300,
issues: 200,
totalRepos: 0,
totalCommits: 0,
contributions: 0,
followers: 0,
prs: 0,
issues: 0,
stargazers: 0,
}),
).toStrictEqual({ level: "A+", score: 49.16605417270399 });
).toStrictEqual({level: "B", score: 100.});
});

it("average user gets A rank", () => {
expect(
calculateRank({
totalRepos: 10,
totalCommits: 500,
contributions: 500,
followers: 50,
prs: 12,
issues: 13,
stargazers: 100,
}),
).toStrictEqual({"A", score: 50.});
francois-rozet marked this conversation as resolved.
Show resolved Hide resolved
});

it("Linus Torvalds gets S+ rank", () => {
expect(
calculateRank({
totalRepos: 4,
totalCommits: 20000,
contributions: 20000,
followers: 132000,
prs: 71,
issues: 2,
stargazers: 109100,
}),
).toStrictEqual({level: "S+", score: 1.8875322153011722});
});
});