Skip to content

Commit b594b57

Browse files
vzskyTouch Sungkawichairickstaaqwerty541Copilot
authored
feat: implement commits year option and display appropriate year label (#2107)
* add custom year * edit fetcher * add tests * add documentation * Update vercel.json * Update fetchStats.test.js * Update readme.md * Update readme.md Co-authored-by: Rick Staa <[email protected]> * test: fix tests * style: format code * fix: fix commit stats time frame description #1053 This commit makes sure the last year label is shown when `include_all_commits` or `year` is not provided. See #1053 for more information. * resolved merge conflict * Update stats-fetcher.js * resolve comments * Update src/cards/types.d.ts Co-authored-by: Alexandr Garbuzov <[email protected]> * Update src/fetchers/stats-fetcher.js Co-authored-by: Alexandr Garbuzov <[email protected]> * graphQL year only to commit * change argument name to commits_year * Update readme.md Co-authored-by: Alexandr Garbuzov <[email protected]> * Update readme.md Co-authored-by: Alexandr Garbuzov <[email protected]> * Update readme.md Co-authored-by: Alexandr Garbuzov <[email protected]> * wrap label into function * review * more review * fix test * Update readme.md Co-authored-by: Copilot <[email protected]> * docs --------- Co-authored-by: Touch Sungkawichai <[email protected]> Co-authored-by: Rick Staa <[email protected]> Co-authored-by: Alexandr Garbuzov <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 63b4021 commit b594b57

File tree

9 files changed

+118
-18
lines changed

9 files changed

+118
-18
lines changed

api/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export default async (req, res) => {
2121
hide_rank,
2222
show_icons,
2323
include_all_commits,
24+
commits_year,
2425
line_height,
2526
title_color,
2627
ring_color,
@@ -98,6 +99,7 @@ export default async (req, res) => {
9899
showStats.includes("prs_merged_percentage"),
99100
showStats.includes("discussions_started"),
100101
showStats.includes("discussions_answered"),
102+
parseInt(commits_year, 10),
101103
);
102104

103105
let cacheSeconds = clampValue(
@@ -123,6 +125,7 @@ export default async (req, res) => {
123125
card_width: parseInt(card_width, 10),
124126
hide_rank: parseBoolean(hide_rank),
125127
include_all_commits: parseBoolean(include_all_commits),
128+
commits_year: parseInt(commits_year, 10),
126129
line_height,
127130
title_color,
128131
ring_color,

readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
- [Hiding individual stats](#hiding-individual-stats)
5353
- [Showing additional individual stats](#showing-additional-individual-stats)
5454
- [Showing icons](#showing-icons)
55+
- [Showing commits count for specified year](#showing-commits-count-for-specified-year)
5556
- [Themes](#themes)
5657
- [Customization](#customization)
5758
- [GitHub Extra Pins](#github-extra-pins)
@@ -149,6 +150,14 @@ To enable icons, you can pass `&show_icons=true` in the query param, like so:
149150
![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true)
150151
```
151152

153+
### Showing commits count for specified year
154+
155+
You can specify a year and fetch only the commits that were made in that year by passing `&commits_year=YYYY` to the parameter.
156+
157+
```md
158+
![Anurag's GitHub stats](https://github-readme-stats.vercel.app/api?username=anuraghazra&commits_year=2020)
159+
```
160+
152161
### Themes
153162

154163
With inbuilt themes, you can customize the look of the card without doing any [manual customization](#customization).
@@ -362,6 +371,7 @@ If we don't support your language, please consider contributing! You can find mo
362371
| `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` |
363372
| `number_format` | Switches between two available formats for displaying the card values `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` |
364373
| `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`). | string (comma-separated values) | `null` |
374+
| `commits_year` | Filters and counts only commits made in the specified year | integer _(YYYY)_ | `<current year> (one year to date)`.
365375

366376
> [!NOTE]\
367377
> When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding.

src/cards/stats.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
kFormatter,
1111
measureText,
1212
} from "../common/utils.js";
13-
import { statCardLocales } from "../translations.js";
13+
import { statCardLocales, wakatimeCardLocales } from "../translations.js";
1414

1515
const CARD_MIN_WIDTH = 287;
1616
const CARD_DEFAULT_WIDTH = 287;
@@ -187,6 +187,21 @@ const getStyles = ({
187187
`;
188188
};
189189

190+
/**
191+
* Return the label for commits according to the selected options
192+
*
193+
* @param {boolean} include_all_commits Option to include all years
194+
* @param {number|undefined} commits_year Option to include only selected year
195+
* @param {I18n} i18n The I18n instance.
196+
* @returns {string} The label corresponding to the options.
197+
*/
198+
const getTotalCommitsYearLabel = (include_all_commits, commits_year, i18n) =>
199+
include_all_commits
200+
? ""
201+
: commits_year
202+
? ` (${commits_year})`
203+
: ` (${i18n.t("wakatimecard.lastyear")})`;
204+
190205
/**
191206
* @typedef {import('../fetchers/types').StatsData} StatsData
192207
* @typedef {import('./types').StatCardOptions} StatCardOptions
@@ -222,6 +237,7 @@ const renderStatsCard = (stats, options = {}) => {
222237
card_width,
223238
hide_rank = false,
224239
include_all_commits = false,
240+
commits_year,
225241
line_height = 25,
226242
title_color,
227243
ring_color,
@@ -257,7 +273,10 @@ const renderStatsCard = (stats, options = {}) => {
257273
const apostrophe = /s$/i.test(name.trim()) ? "" : "s";
258274
const i18n = new I18n({
259275
locale,
260-
translations: statCardLocales({ name, apostrophe }),
276+
translations: {
277+
...statCardLocales({ name, apostrophe }),
278+
...wakatimeCardLocales,
279+
},
261280
});
262281

263282
// Meta data for creating text nodes with createTextNode function
@@ -271,9 +290,11 @@ const renderStatsCard = (stats, options = {}) => {
271290
};
272291
STATS.commits = {
273292
icon: icons.commits,
274-
label: `${i18n.t("statcard.commits")}${
275-
include_all_commits ? "" : ` (${new Date().getFullYear()})`
276-
}`,
293+
label: `${i18n.t("statcard.commits")}${getTotalCommitsYearLabel(
294+
include_all_commits,
295+
commits_year,
296+
i18n,
297+
)}`,
277298
value: totalCommits,
278299
id: "commits",
279300
};
@@ -515,9 +536,11 @@ const renderStatsCard = (stats, options = {}) => {
515536
.filter((key) => !hide.includes(key))
516537
.map((key) => {
517538
if (key === "commits") {
518-
return `${i18n.t("statcard.commits")} ${
519-
include_all_commits ? "" : `in ${new Date().getFullYear()}`
520-
} : ${STATS[key].value}`;
539+
return `${i18n.t("statcard.commits")} ${getTotalCommitsYearLabel(
540+
include_all_commits,
541+
commits_year,
542+
i18n,
543+
)} : ${STATS[key].value}`;
521544
}
522545
return `${STATS[key].label}: ${STATS[key].value}`;
523546
})

src/cards/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type StatCardOptions = CommonOptions & {
2020
card_width: number;
2121
hide_rank: boolean;
2222
include_all_commits: boolean;
23+
commits_year: number;
2324
line_height: number | string;
2425
custom_title: string;
2526
disable_animations: boolean;

src/fetchers/stats.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ const GRAPHQL_REPOS_QUERY = `
4040
`;
4141

4242
const GRAPHQL_STATS_QUERY = `
43-
query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!) {
43+
query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null) {
4444
user(login: $login) {
4545
name
4646
login
47-
contributionsCollection {
47+
commits: contributionsCollection (from: $startTime) {
4848
totalCommitContributions,
49+
}
50+
reviews: contributionsCollection {
4951
totalPullRequestReviewContributions
5052
}
5153
repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
@@ -109,6 +111,7 @@ const fetcher = (variables, token) => {
109111
* @param {boolean} variables.includeMergedPullRequests Include merged pull requests.
110112
* @param {boolean} variables.includeDiscussions Include discussions.
111113
* @param {boolean} variables.includeDiscussionsAnswers Include discussions answers.
114+
* @param {string|undefined} variables.startTime Time to start the count of total commits.
112115
* @returns {Promise<AxiosResponse>} Axios response.
113116
*
114117
* @description This function supports multi-page fetching if the 'FETCH_MULTI_PAGE_STARS' environment variable is set to true.
@@ -118,6 +121,7 @@ const statsFetcher = async ({
118121
includeMergedPullRequests,
119122
includeDiscussions,
120123
includeDiscussionsAnswers,
124+
startTime,
121125
}) => {
122126
let stats;
123127
let hasNextPage = true;
@@ -130,6 +134,7 @@ const statsFetcher = async ({
130134
includeMergedPullRequests,
131135
includeDiscussions,
132136
includeDiscussionsAnswers,
137+
startTime,
133138
};
134139
let res = await retryer(fetcher, variables);
135140
if (res.data.errors) {
@@ -217,6 +222,7 @@ const totalCommitsFetcher = async (username) => {
217222
* @param {boolean} include_merged_pull_requests Include merged pull requests.
218223
* @param {boolean} include_discussions Include discussions.
219224
* @param {boolean} include_discussions_answers Include discussions answers.
225+
* @param {number|undefined} commits_year Year to count total commits
220226
* @returns {Promise<StatsData>} Stats data.
221227
*/
222228
const fetchStats = async (
@@ -226,6 +232,7 @@ const fetchStats = async (
226232
include_merged_pull_requests = false,
227233
include_discussions = false,
228234
include_discussions_answers = false,
235+
commits_year,
229236
) => {
230237
if (!username) {
231238
throw new MissingParamError(["username"]);
@@ -251,6 +258,7 @@ const fetchStats = async (
251258
includeMergedPullRequests: include_merged_pull_requests,
252259
includeDiscussions: include_discussions,
253260
includeDiscussionsAnswers: include_discussions_answers,
261+
startTime: commits_year ? `${commits_year}-01-01T00:00:00Z` : undefined,
254262
});
255263

256264
// Catch GraphQL errors.
@@ -282,7 +290,7 @@ const fetchStats = async (
282290
if (include_all_commits) {
283291
stats.totalCommits = await totalCommitsFetcher(username);
284292
} else {
285-
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
293+
stats.totalCommits = user.commits.totalCommitContributions;
286294
}
287295

288296
stats.totalPRs = user.pullRequests.totalCount;
@@ -291,8 +299,7 @@ const fetchStats = async (
291299
stats.mergedPRsPercentage =
292300
(user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100;
293301
}
294-
stats.totalReviews =
295-
user.contributionsCollection.totalPullRequestReviewContributions;
302+
stats.totalReviews = user.reviews.totalPullRequestReviewContributions;
296303
stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount;
297304
if (include_discussions) {
298305
stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount;

tests/api.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ const data_stats = {
3838
user: {
3939
name: stats.name,
4040
repositoriesContributedTo: { totalCount: stats.contributedTo },
41-
contributionsCollection: {
41+
commits: {
4242
totalCommitContributions: stats.totalCommits,
43+
},
44+
reviews: {
4345
totalPullRequestReviewContributions: stats.totalReviews,
4446
},
4547
pullRequests: { totalCount: stats.totalPRs },

tests/bench/api.bench.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ const data_stats = {
2424
user: {
2525
name: stats.name,
2626
repositoriesContributedTo: { totalCount: stats.contributedTo },
27-
contributionsCollection: {
27+
commits: {
2828
totalCommitContributions: stats.totalCommits,
29+
},
30+
reviews: {
2931
totalPullRequestReviewContributions: stats.totalReviews,
3032
},
3133
pullRequests: { totalCount: stats.totalPRs },

tests/fetchStats.test.js

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ const data_stats = {
1111
user: {
1212
name: "Anurag Hazra",
1313
repositoriesContributedTo: { totalCount: 61 },
14-
contributionsCollection: {
14+
commits: {
1515
totalCommitContributions: 100,
16+
},
17+
reviews: {
1618
totalPullRequestReviewContributions: 50,
1719
},
1820
pullRequests: { totalCount: 300 },
@@ -38,6 +40,9 @@ const data_stats = {
3840
},
3941
};
4042

43+
const data_year2003 = JSON.parse(JSON.stringify(data_stats));
44+
data_year2003.data.user.commits.totalCommitContributions = 428;
45+
4146
const data_repo = {
4247
data: {
4348
user: {
@@ -91,9 +96,18 @@ const mock = new MockAdapter(axios);
9196
beforeEach(() => {
9297
process.env.FETCH_MULTI_PAGE_STARS = "false"; // Set to `false` to fetch only one page of stars.
9398
mock.onPost("https://api.github.com/graphql").reply((cfg) => {
99+
let req = JSON.parse(cfg.data);
100+
101+
if (
102+
req.variables &&
103+
req.variables.startTime &&
104+
req.variables.startTime.startsWith("2003")
105+
) {
106+
return [200, data_year2003];
107+
}
94108
return [
95109
200,
96-
cfg.data.includes("contributionsCollection") ? data_stats : data_repo,
110+
req.query.includes("totalCommitContributions") ? data_stats : data_repo,
97111
];
98112
});
99113
});
@@ -409,4 +423,42 @@ describe("Test fetchStats", () => {
409423
rank,
410424
});
411425
});
426+
427+
it("should get commits of provided year", async () => {
428+
let stats = await fetchStats(
429+
"anuraghazra",
430+
false,
431+
[],
432+
false,
433+
false,
434+
false,
435+
2003,
436+
);
437+
438+
const rank = calculateRank({
439+
all_commits: false,
440+
commits: 428,
441+
prs: 300,
442+
reviews: 50,
443+
issues: 200,
444+
repos: 5,
445+
stars: 300,
446+
followers: 100,
447+
});
448+
449+
expect(stats).toStrictEqual({
450+
contributedTo: 61,
451+
name: "Anurag Hazra",
452+
totalCommits: 428,
453+
totalIssues: 200,
454+
totalPRs: 300,
455+
totalPRsMerged: 0,
456+
mergedPRsPercentage: 0,
457+
totalReviews: 50,
458+
totalStars: 300,
459+
totalDiscussionsStarted: 0,
460+
totalDiscussionsAnswered: 0,
461+
rank,
462+
});
463+
});
412464
});

tests/renderStatsCard.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ describe("Test renderStatsCard", () => {
387387
document.querySelector(
388388
'g[transform="translate(0, 25)"]>.stagger>.stat.bold',
389389
).textContent,
390-
).toMatchInlineSnapshot(`"累计提交总数 (${new Date().getFullYear()}):"`);
390+
).toMatchInlineSnapshot(`"累计提交总数 (去年):"`);
391391
expect(
392392
document.querySelector(
393393
'g[transform="translate(0, 50)"]>.stagger>.stat.bold',

0 commit comments

Comments
 (0)