From 416f027faea5ee00afeaac6a114ae15fc70b8868 Mon Sep 17 00:00:00 2001 From: Anurag Hazra Date: Fri, 31 Jul 2020 13:37:39 +0530 Subject: [PATCH] fix: total commit counts (#211) * fix: wip fix total commit counts * tests: added tests * chore: remove console logs * docs: added docs for include_all_commits * chore: increased value offset x * chore: added reference/links comments * docs: updated docs --- api/index.js | 8 +++++- package.json | 1 + readme.md | 5 ++++ src/fetchStats.js | 56 ++++++++++++++++++++++++++++++++++++---- src/renderStatsCard.js | 23 ++++++++++++++--- tests/fetchStats.test.js | 35 +++++++++++++++++++++++-- 6 files changed, 117 insertions(+), 11 deletions(-) diff --git a/api/index.js b/api/index.js index a17387c..b16990c 100644 --- a/api/index.js +++ b/api/index.js @@ -18,6 +18,7 @@ module.exports = async (req, res) => { hide_rank, show_icons, count_private, + include_all_commits, line_height, title_color, icon_color, @@ -31,7 +32,11 @@ module.exports = async (req, res) => { res.setHeader("Content-Type", "image/svg+xml"); try { - stats = await fetchStats(username, parseBoolean(count_private)); + stats = await fetchStats( + username, + parseBoolean(count_private), + parseBoolean(include_all_commits) + ); } catch (err) { return res.send( renderError( @@ -56,6 +61,7 @@ module.exports = async (req, res) => { hide_title: parseBoolean(hide_title), hide_border: parseBoolean(hide_border), hide_rank: parseBoolean(hide_rank), + include_all_commits: parseBoolean(include_all_commits), line_height, title_color, icon_color, diff --git a/package.json b/package.json index 0adc010..d167c1b 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "dotenv": "^8.2.0", "emoji-name-map": "^1.2.8", + "github-username-regex": "^1.0.0", "word-wrap": "^1.2.3" }, "husky": { diff --git a/readme.md b/readme.md index 44d263c..55e17e4 100644 --- a/readme.md +++ b/readme.md @@ -135,6 +135,7 @@ You can customize the appearance of your `Stats Card` or `Repo Card` however you - `hide_title` - _(boolean)_ - `hide_rank` - _(boolean)_ - `show_icons` - _(boolean)_ +- `include_total_commits` - Count total commits instead of just the current year commits _(boolean)_ - `count_private` - Count private commits _(boolean)_ - `line_height` - Sets the line-height between text _(number)_ @@ -235,6 +236,10 @@ You can use the `&layout=compact` option to change the card design. ![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true) +- Include All Commits + +![Anurag's github stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&include_all_commits=true) + - Themes Choose from any of the [default themes](#themes) diff --git a/src/fetchStats.js b/src/fetchStats.js index 66df9d0..711d59e 100644 --- a/src/fetchStats.js +++ b/src/fetchStats.js @@ -1,6 +1,9 @@ const { request, logger } = require("./utils"); +const axios = require("axios"); const retryer = require("./retryer"); const calculateRank = require("./calculateRank"); +const githubUsernameRegex = require("github-username-regex"); + require("dotenv").config(); const fetcher = (variables, token) => { @@ -46,7 +49,45 @@ const fetcher = (variables, token) => { ); }; -async function fetchStats(username, count_private = false) { +// https://github.com/anuraghazra/github-readme-stats/issues/92#issuecomment-661026467 +// https://github.com/anuraghazra/github-readme-stats/pull/211/ +const totalCommitsFetcher = async (username) => { + if (!githubUsernameRegex.test(username)) { + logger.log("Invalid username"); + return 0; + } + + // https://developer.github.com/v3/search/#search-commits + const fetchTotalCommits = (variables, token) => { + return axios({ + method: "get", + url: `https://api.github.com/search/commits?q=author:${variables.login}`, + headers: { + "Content-Type": "application/json", + Accept: "application/vnd.github.cloak-preview", + Authorization: `bearer ${token}`, + }, + }); + }; + + try { + let res = await retryer(fetchTotalCommits, { login: username }); + if (res.data.total_count) { + return res.data.total_count; + } + } catch (err) { + logger.log(err); + // just return 0 if there is something wrong so that + // we don't break the whole app + return 0; + } +}; + +async function fetchStats( + username, + count_private = false, + include_all_commits = false +) { if (!username) throw Error("Invalid username"); const stats = { @@ -61,6 +102,11 @@ async function fetchStats(username, count_private = false) { let res = await retryer(fetcher, { login: username }); + let experimental_totalCommits = 0; + if (include_all_commits) { + experimental_totalCommits = await totalCommitsFetcher(username); + } + if (res.data.errors) { logger.error(res.data.errors); throw Error(res.data.errors[0].message || "Could not fetch user"); @@ -72,11 +118,11 @@ async function fetchStats(username, count_private = false) { stats.name = user.name || user.login; stats.totalIssues = user.issues.totalCount; - stats.totalCommits = contributionCount.totalCommitContributions; + stats.totalCommits = + contributionCount.totalCommitContributions + experimental_totalCommits; + if (count_private) { - stats.totalCommits = - contributionCount.totalCommitContributions + - contributionCount.restrictedContributionsCount; + stats.totalCommits += contributionCount.restrictedContributionsCount; } stats.totalPRs = user.pullRequests.totalCount; diff --git a/src/renderStatsCard.js b/src/renderStatsCard.js index ace6bcb..b5e41d2 100644 --- a/src/renderStatsCard.js +++ b/src/renderStatsCard.js @@ -8,7 +8,15 @@ const { getStyles } = require("./getStyles"); const icons = require("./icons"); const Card = require("./Card"); -const createTextNode = ({ icon, label, value, id, index, showIcons }) => { +const createTextNode = ({ + icon, + label, + value, + id, + index, + showIcons, + shiftValuePos, +}) => { const kValue = kFormatter(value); const staggerDelay = (index + 3) * 150; @@ -24,7 +32,12 @@ const createTextNode = ({ icon, label, value, id, index, showIcons }) => { ${iconSvg} ${label}: - ${kValue} + ${kValue} `; }; @@ -45,6 +58,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { hide_title = false, hide_border = false, hide_rank = false, + include_all_commits = false, line_height = 25, title_color, icon_color, @@ -74,7 +88,9 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { }, commits: { icon: icons.commits, - label: "Total Commits", + label: `Total Commits${ + include_all_commits ? "" : ` (${new Date().getFullYear()})` + }`, value: totalCommits, id: "commits", }, @@ -107,6 +123,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => { ...STATS[key], index, showIcons: show_icons, + shiftValuePos: !include_all_commits, }) ); diff --git a/tests/fetchStats.test.js b/tests/fetchStats.test.js index 34e7781..b63312f 100644 --- a/tests/fetchStats.test.js +++ b/tests/fetchStats.test.js @@ -9,7 +9,10 @@ const data = { user: { name: "Anurag Hazra", repositoriesContributedTo: { totalCount: 61 }, - contributionsCollection: { totalCommitContributions: 100, restrictedContributionsCount: 50 }, + contributionsCollection: { + totalCommitContributions: 100, + restrictedContributionsCount: 50, + }, pullRequests: { totalCount: 300 }, issues: { totalCount: 200 }, followers: { totalCount: 100 }, @@ -102,4 +105,32 @@ describe("Test fetchStats", () => { rank, }); }); -}); \ No newline at end of file + + it("should fetch total commits", async () => { + mock.onPost("https://api.github.com/graphql").reply(200, data); + mock + .onGet("https://api.github.com/search/commits?q=author:anuraghazra") + .reply(200, { total_count: 1000 }); + + let stats = await fetchStats("anuraghazra", true, true); + const rank = calculateRank({ + totalCommits: 1000 + 150, + totalRepos: 5, + followers: 100, + contributions: 61, + stargazers: 400, + prs: 300, + issues: 200, + }); + + expect(stats).toStrictEqual({ + contributedTo: 61, + name: "Anurag Hazra", + totalCommits: 1000 + 150, + totalIssues: 200, + totalPRs: 300, + totalStars: 400, + rank, + }); + }); +});