diff --git a/readme.md b/readme.md index c4d9c8d..fc38996 100644 --- a/readme.md +++ b/readme.md @@ -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 diff --git a/src/calculateRank.js b/src/calculateRank.js index 0320006..836779e 100644 --- a/src/calculateRank.js +++ b/src/calculateRank.js @@ -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; diff --git a/src/fetchers/stats-fetcher.js b/src/fetchers/stats-fetcher.js index 21d2611..2e79962 100644 --- a/src/fetchers/stats-fetcher.js +++ b/src/fetchers/stats-fetcher.js @@ -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, diff --git a/tests/api.test.js b/tests/api.test.js index 2b013d9..3c72583 100644 --- a/tests/api.test.js +++ b/tests/api.test.js @@ -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, diff --git a/tests/calculateRank.test.js b/tests/calculateRank.test.js index a91299c..65f60df 100644 --- a/tests/calculateRank.test.js +++ b/tests/calculateRank.test.js @@ -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 }); }); }); diff --git a/tests/e2e/e2e.test.js b/tests/e2e/e2e.test.js index c32db6c..e220d52 100644 --- a/tests/e2e/e2e.test.js +++ b/tests/e2e/e2e.test.js @@ -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, }, }; diff --git a/tests/fetchStats.test.js b/tests/fetchStats.test.js index 7cb4b39..80b19bf 100644 --- a/tests/fetchStats.test.js +++ b/tests/fetchStats.test.js @@ -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,