forked from mirrored/github-readme-stats
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1afe4189cb |
+25
-29
@@ -1,5 +1,10 @@
|
|||||||
function expsf(x, lambda = 1) {
|
function exponential_cdf(x) {
|
||||||
return 2 ** (-lambda * x);
|
return 1 - 2 ** -x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_normal_cdf(x) {
|
||||||
|
// approximation
|
||||||
|
return x / (1 + x);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +18,7 @@ function expsf(x, lambda = 1) {
|
|||||||
* @param {number} params.repos Total number of repos.
|
* @param {number} params.repos Total number of repos.
|
||||||
* @param {number} params.stars The number of stars.
|
* @param {number} params.stars The number of stars.
|
||||||
* @param {number} params.followers The number of followers.
|
* @param {number} params.followers The number of followers.
|
||||||
* @returns {{level: string, score: number}}} The users rank.
|
* @returns {{level: string, percentile: number}}} The users rank.
|
||||||
*/
|
*/
|
||||||
function calculateRank({
|
function calculateRank({
|
||||||
all_commits,
|
all_commits,
|
||||||
@@ -24,15 +29,15 @@ function calculateRank({
|
|||||||
stars,
|
stars,
|
||||||
followers,
|
followers,
|
||||||
}) {
|
}) {
|
||||||
const COMMITS_MEAN = all_commits ? 1000 : 250,
|
const COMMITS_MEDIAN = all_commits ? 1000 : 250,
|
||||||
COMMITS_WEIGHT = 2;
|
COMMITS_WEIGHT = 2;
|
||||||
const PRS_MEAN = 50,
|
const PRS_MEDIAN = 50,
|
||||||
PRS_WEIGHT = 3;
|
PRS_WEIGHT = 3;
|
||||||
const ISSUES_MEAN = 25,
|
const ISSUES_MEDIAN = 25,
|
||||||
ISSUES_WEIGHT = 1;
|
ISSUES_WEIGHT = 1;
|
||||||
const STARS_MEAN = 250,
|
const STARS_MEDIAN = 50,
|
||||||
STARS_WEIGHT = 4;
|
STARS_WEIGHT = 4;
|
||||||
const FOLLOWERS_MEAN = 25,
|
const FOLLOWERS_MEDIAN = 10,
|
||||||
FOLLOWERS_WEIGHT = 1;
|
FOLLOWERS_WEIGHT = 1;
|
||||||
|
|
||||||
const TOTAL_WEIGHT =
|
const TOTAL_WEIGHT =
|
||||||
@@ -42,30 +47,21 @@ function calculateRank({
|
|||||||
STARS_WEIGHT +
|
STARS_WEIGHT +
|
||||||
FOLLOWERS_WEIGHT;
|
FOLLOWERS_WEIGHT;
|
||||||
|
|
||||||
|
const THRESHOLDS = [1, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100];
|
||||||
|
const LEVELS = ["S", "A+", "A", "A-", "B+", "B", "B-", "C+", "C"];
|
||||||
|
|
||||||
const rank =
|
const rank =
|
||||||
(COMMITS_WEIGHT * expsf(commits, 1 / COMMITS_MEAN) +
|
1 -
|
||||||
PRS_WEIGHT * expsf(prs, 1 / PRS_MEAN) +
|
(COMMITS_WEIGHT * exponential_cdf(commits / COMMITS_MEDIAN) +
|
||||||
ISSUES_WEIGHT * expsf(issues, 1 / ISSUES_MEAN) +
|
PRS_WEIGHT * exponential_cdf(prs / PRS_MEDIAN) +
|
||||||
STARS_WEIGHT * expsf(stars, 1 / STARS_MEAN) +
|
ISSUES_WEIGHT * exponential_cdf(issues / ISSUES_MEDIAN) +
|
||||||
FOLLOWERS_WEIGHT * expsf(followers, 1 / FOLLOWERS_MEAN)) /
|
STARS_WEIGHT * log_normal_cdf(stars / STARS_MEDIAN) +
|
||||||
TOTAL_WEIGHT;
|
FOLLOWERS_WEIGHT * log_normal_cdf(followers / FOLLOWERS_MEDIAN)) /
|
||||||
|
TOTAL_WEIGHT;
|
||||||
|
|
||||||
const RANK_S_PLUS = 0.025;
|
const level = LEVELS[THRESHOLDS.findIndex((t) => rank * 100 <= t)];
|
||||||
const RANK_S = 0.1;
|
|
||||||
const RANK_A_PLUS = 0.25;
|
|
||||||
const RANK_A = 0.5;
|
|
||||||
const RANK_B_PLUS = 0.75;
|
|
||||||
|
|
||||||
const level = (() => {
|
return { level: level, percentile: rank * 100 };
|
||||||
if (rank <= RANK_S_PLUS) return "S+";
|
|
||||||
if (rank <= RANK_S) return "S";
|
|
||||||
if (rank <= RANK_A_PLUS) return "A+";
|
|
||||||
if (rank <= RANK_A) return "A";
|
|
||||||
if (rank <= RANK_B_PLUS) return "B+";
|
|
||||||
return "B";
|
|
||||||
})();
|
|
||||||
|
|
||||||
return { level, score: rank * 100 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { calculateRank };
|
export { calculateRank };
|
||||||
|
|||||||
@@ -209,9 +209,8 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
|||||||
hide_rank ? 0 : 150,
|
hide_rank ? 0 : 150,
|
||||||
);
|
);
|
||||||
|
|
||||||
// the better user's score the the rank will be closer to zero so
|
// the lower the user's percentile the better
|
||||||
// subtracting 100 to get the progress in 100%
|
const progress = 100 - rank.percentile;
|
||||||
const progress = 100 - rank.score;
|
|
||||||
const cssStyles = getStyles({
|
const cssStyles = getStyles({
|
||||||
titleColor,
|
titleColor,
|
||||||
ringColor,
|
ringColor,
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ const fetchStats = async (
|
|||||||
totalIssues: 0,
|
totalIssues: 0,
|
||||||
totalStars: 0,
|
totalStars: 0,
|
||||||
contributedTo: 0,
|
contributedTo: 0,
|
||||||
rank: { level: "B", score: 0 },
|
rank: { level: "C", percentile: 100 },
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = await statsFetcher(username);
|
let res = await statsFetcher(username);
|
||||||
|
|||||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ export type StatsData = {
|
|||||||
totalIssues: number;
|
totalIssues: number;
|
||||||
totalStars: number;
|
totalStars: number;
|
||||||
contributedTo: number;
|
contributedTo: number;
|
||||||
rank: { level: string; score: number };
|
rank: { level: string; percentile: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Lang = {
|
export type Lang = {
|
||||||
|
|||||||
+39
-25
@@ -2,7 +2,7 @@ import "@testing-library/jest-dom";
|
|||||||
import { calculateRank } from "../src/calculateRank.js";
|
import { calculateRank } from "../src/calculateRank.js";
|
||||||
|
|
||||||
describe("Test calculateRank", () => {
|
describe("Test calculateRank", () => {
|
||||||
it("new user gets B rank", () => {
|
it("new user gets C rank", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: false,
|
all_commits: false,
|
||||||
@@ -13,10 +13,24 @@ describe("Test calculateRank", () => {
|
|||||||
stars: 0,
|
stars: 0,
|
||||||
followers: 0,
|
followers: 0,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "B", score: 100 });
|
).toStrictEqual({ level: "C", percentile: 100 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("average user gets A rank", () => {
|
it("beginner user gets B- rank", () => {
|
||||||
|
expect(
|
||||||
|
calculateRank({
|
||||||
|
all_commits: false,
|
||||||
|
commits: 125,
|
||||||
|
prs: 25,
|
||||||
|
issues: 10,
|
||||||
|
repos: 0,
|
||||||
|
stars: 25,
|
||||||
|
followers: 5,
|
||||||
|
}),
|
||||||
|
).toStrictEqual({ level: "B-", percentile: 69.333868386557 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("median user gets B+ rank", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: false,
|
all_commits: false,
|
||||||
@@ -24,13 +38,13 @@ describe("Test calculateRank", () => {
|
|||||||
prs: 50,
|
prs: 50,
|
||||||
issues: 25,
|
issues: 25,
|
||||||
repos: 0,
|
repos: 0,
|
||||||
stars: 250,
|
stars: 50,
|
||||||
followers: 25,
|
followers: 10,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "A", score: 50 });
|
).toStrictEqual({ level: "B+", percentile: 50 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("average user gets A rank (include_all_commits)", () => {
|
it("average user gets B+ rank (include_all_commits)", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: true,
|
all_commits: true,
|
||||||
@@ -38,13 +52,13 @@ describe("Test calculateRank", () => {
|
|||||||
prs: 50,
|
prs: 50,
|
||||||
issues: 25,
|
issues: 25,
|
||||||
repos: 0,
|
repos: 0,
|
||||||
stars: 250,
|
stars: 50,
|
||||||
followers: 25,
|
followers: 10,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "A", score: 50 });
|
).toStrictEqual({ level: "B+", percentile: 50 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("more than average user gets A+ rank", () => {
|
it("advanced user gets A rank", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: false,
|
all_commits: false,
|
||||||
@@ -52,13 +66,13 @@ describe("Test calculateRank", () => {
|
|||||||
prs: 100,
|
prs: 100,
|
||||||
issues: 50,
|
issues: 50,
|
||||||
repos: 0,
|
repos: 0,
|
||||||
stars: 500,
|
stars: 200,
|
||||||
followers: 50,
|
followers: 40,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "A+", score: 25 });
|
).toStrictEqual({ level: "A", percentile: 22.72727272727273 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("expert user gets S rank", () => {
|
it("expert user gets A+ rank", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: false,
|
all_commits: false,
|
||||||
@@ -66,23 +80,23 @@ describe("Test calculateRank", () => {
|
|||||||
prs: 200,
|
prs: 200,
|
||||||
issues: 100,
|
issues: 100,
|
||||||
repos: 0,
|
repos: 0,
|
||||||
stars: 1000,
|
stars: 800,
|
||||||
followers: 100,
|
followers: 160,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "S", score: 6.25 });
|
).toStrictEqual({ level: "A+", percentile: 6.082887700534744 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("ezyang gets S+ rank", () => {
|
it("sindresorhus gets S rank", () => {
|
||||||
expect(
|
expect(
|
||||||
calculateRank({
|
calculateRank({
|
||||||
all_commits: false,
|
all_commits: false,
|
||||||
commits: 1000,
|
commits: 1300,
|
||||||
prs: 4000,
|
prs: 1500,
|
||||||
issues: 2000,
|
issues: 4500,
|
||||||
repos: 0,
|
repos: 0,
|
||||||
stars: 5000,
|
stars: 600000,
|
||||||
followers: 2000,
|
followers: 50000,
|
||||||
}),
|
}),
|
||||||
).toStrictEqual({ level: "S+", score: 1.1363983154296875 });
|
).toStrictEqual({ level: "S", percentile: 0.49947889605312934 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user