Ranks: Take into account user reviewed PRs count (#2857)

* Rank: Take into account user reviewed PRs count

* e2e

* fix tests

* dev

* docs

* dev

* dev
This commit is contained in:
Alexandr Garbuzov 2023-07-21 14:59:53 +03:00 committed by GitHub
parent 0c380e3982
commit b56689b4bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 31 additions and 8 deletions

View File

@ -140,7 +140,7 @@ Change the `?username=` value to your GitHub username.
> By default, the stats card only shows statistics like stars, commits and pull requests from public repositories. To show private statistics on the stats card, you should [deploy your own instance](#deploy-on-your-own) using your own GitHub API token.
> **Note**
> Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, issues, stars and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [src/calculateRank.js](./src/calculateRank.js). The circle around the rank shows 100 minus the global percentile.
> Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [src/calculateRank.js](./src/calculateRank.js). The circle around the rank shows 100 minus the global percentile.
### Hiding individual stats

View File

@ -15,6 +15,7 @@ function log_normal_cdf(x) {
* @param {number} params.commits Number of commits.
* @param {number} params.prs The number of pull requests.
* @param {number} params.issues The number of issues.
* @param {number} params.reviews The number of reviews.
* @param {number} params.repos Total number of repos.
* @param {number} params.stars The number of stars.
* @param {number} params.followers The number of followers.
@ -25,6 +26,7 @@ function calculateRank({
commits,
prs,
issues,
reviews,
// eslint-disable-next-line no-unused-vars
repos, // unused
stars,
@ -36,6 +38,8 @@ function calculateRank({
PRS_WEIGHT = 3;
const ISSUES_MEDIAN = 25,
ISSUES_WEIGHT = 1;
const REVIEWS_MEDIAN = 2,
REVIEWS_WEIGHT = 1;
const STARS_MEDIAN = 50,
STARS_WEIGHT = 4;
const FOLLOWERS_MEDIAN = 10,
@ -45,6 +49,7 @@ function calculateRank({
COMMITS_WEIGHT +
PRS_WEIGHT +
ISSUES_WEIGHT +
REVIEWS_WEIGHT +
STARS_WEIGHT +
FOLLOWERS_WEIGHT;
@ -56,6 +61,7 @@ function calculateRank({
(COMMITS_WEIGHT * exponential_cdf(commits / COMMITS_MEDIAN) +
PRS_WEIGHT * exponential_cdf(prs / PRS_MEDIAN) +
ISSUES_WEIGHT * exponential_cdf(issues / ISSUES_MEDIAN) +
REVIEWS_WEIGHT * exponential_cdf(reviews / REVIEWS_MEDIAN) +
STARS_WEIGHT * log_normal_cdf(stars / STARS_MEDIAN) +
FOLLOWERS_WEIGHT * log_normal_cdf(followers / FOLLOWERS_MEDIAN)) /
TOTAL_WEIGHT;

View File

@ -259,6 +259,7 @@ const fetchStats = async (
all_commits: include_all_commits,
commits: stats.totalCommits,
prs: stats.totalPRs,
reviews: stats.totalReviews,
issues: stats.totalIssues,
repos: user.repositories.totalCount,
stars: stats.totalStars,

View File

@ -24,6 +24,7 @@ stats.rank = calculateRank({
all_commits: false,
commits: stats.totalCommits,
prs: stats.totalPRs,
reviews: stats.totalReviews,
issues: stats.totalIssues,
repos: 1,
stars: stats.totalStars,

View File

@ -10,6 +10,7 @@ describe("Test calculateRank", () => {
commits: 0,
prs: 0,
issues: 0,
reviews: 0,
repos: 0,
stars: 0,
followers: 0,
@ -24,11 +25,12 @@ describe("Test calculateRank", () => {
commits: 125,
prs: 25,
issues: 10,
reviews: 5,
repos: 0,
stars: 25,
followers: 5,
}),
).toStrictEqual({ level: "B-", percentile: 69.333868386557 });
).toStrictEqual({ level: "B-", percentile: 65.02918514848255 });
});
it("median user gets B+ rank", () => {
@ -38,11 +40,12 @@ describe("Test calculateRank", () => {
commits: 250,
prs: 50,
issues: 25,
reviews: 10,
repos: 0,
stars: 50,
followers: 10,
}),
).toStrictEqual({ level: "B+", percentile: 50 });
).toStrictEqual({ level: "B+", percentile: 46.09375 });
});
it("average user gets B+ rank (include_all_commits)", () => {
@ -52,11 +55,12 @@ describe("Test calculateRank", () => {
commits: 1000,
prs: 50,
issues: 25,
reviews: 10,
repos: 0,
stars: 50,
followers: 10,
}),
).toStrictEqual({ level: "B+", percentile: 50 });
).toStrictEqual({ level: "B+", percentile: 46.09375 });
});
it("advanced user gets A rank", () => {
@ -66,11 +70,12 @@ describe("Test calculateRank", () => {
commits: 500,
prs: 100,
issues: 50,
reviews: 20,
repos: 0,
stars: 200,
followers: 40,
}),
).toStrictEqual({ level: "A", percentile: 22.72727272727273 });
).toStrictEqual({ level: "A", percentile: 20.841471354166664 });
});
it("expert user gets A+ rank", () => {
@ -80,11 +85,12 @@ describe("Test calculateRank", () => {
commits: 1000,
prs: 200,
issues: 100,
reviews: 40,
repos: 0,
stars: 800,
followers: 160,
}),
).toStrictEqual({ level: "A+", percentile: 6.082887700534744 });
).toStrictEqual({ level: "A+", percentile: 5.575988339442828 });
});
it("sindresorhus gets S rank", () => {
@ -94,10 +100,11 @@ describe("Test calculateRank", () => {
commits: 1300,
prs: 1500,
issues: 4500,
reviews: 1000,
repos: 0,
stars: 600000,
followers: 50000,
}),
).toStrictEqual({ level: "S", percentile: 0.49947889605312934 });
).toStrictEqual({ level: "S", percentile: 0.4578556547153667 });
});
});

View File

@ -16,13 +16,14 @@ const USER = "catelinemnemosyne";
const STATS_DATA = {
name: "Cateline Mnemosyne",
totalPRs: 2,
totalReviews: 0,
totalCommits: 8,
totalIssues: 1,
totalStars: 1,
contributedTo: 1,
rank: {
level: "C",
percentile: 97.89377603631637,
percentile: 98.06929469995667,
},
};

View File

@ -108,6 +108,7 @@ describe("Test fetchStats", () => {
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
@ -141,6 +142,7 @@ describe("Test fetchStats", () => {
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
@ -180,6 +182,7 @@ describe("Test fetchStats", () => {
all_commits: true,
commits: 1000,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
@ -210,6 +213,7 @@ describe("Test fetchStats", () => {
all_commits: true,
commits: 1000,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 200,
@ -238,6 +242,7 @@ describe("Test fetchStats", () => {
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 400,
@ -266,6 +271,7 @@ describe("Test fetchStats", () => {
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,
@ -294,6 +300,7 @@ describe("Test fetchStats", () => {
all_commits: false,
commits: 100,
prs: 300,
reviews: 50,
issues: 200,
repos: 5,
stars: 300,