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 <rick.staa@outlook.com> * 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 <qwerty541zxc@gmail.com> * Update src/fetchers/stats-fetcher.js Co-authored-by: Alexandr Garbuzov <qwerty541zxc@gmail.com> * graphQL year only to commit * change argument name to commits_year * Update readme.md Co-authored-by: Alexandr Garbuzov <qwerty541zxc@gmail.com> * Update readme.md Co-authored-by: Alexandr Garbuzov <qwerty541zxc@gmail.com> * Update readme.md Co-authored-by: Alexandr Garbuzov <qwerty541zxc@gmail.com> * wrap label into function * review * more review * fix test * Update readme.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * docs --------- Co-authored-by: Touch Sungkawichai <talay@10ninox.com> Co-authored-by: Rick Staa <rick.staa@outlook.com> Co-authored-by: Alexandr Garbuzov <qwerty541zxc@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Alexandr Garbuzov
Copilot
Touch Sungkawichai
Rick Staa
parent
63b402177d
commit
b594b57b56
@@ -21,6 +21,7 @@ export default async (req, res) => {
|
||||
hide_rank,
|
||||
show_icons,
|
||||
include_all_commits,
|
||||
commits_year,
|
||||
line_height,
|
||||
title_color,
|
||||
ring_color,
|
||||
@@ -98,6 +99,7 @@ export default async (req, res) => {
|
||||
showStats.includes("prs_merged_percentage"),
|
||||
showStats.includes("discussions_started"),
|
||||
showStats.includes("discussions_answered"),
|
||||
parseInt(commits_year, 10),
|
||||
);
|
||||
|
||||
let cacheSeconds = clampValue(
|
||||
@@ -123,6 +125,7 @@ export default async (req, res) => {
|
||||
card_width: parseInt(card_width, 10),
|
||||
hide_rank: parseBoolean(hide_rank),
|
||||
include_all_commits: parseBoolean(include_all_commits),
|
||||
commits_year: parseInt(commits_year, 10),
|
||||
line_height,
|
||||
title_color,
|
||||
ring_color,
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
- [Hiding individual stats](#hiding-individual-stats)
|
||||
- [Showing additional individual stats](#showing-additional-individual-stats)
|
||||
- [Showing icons](#showing-icons)
|
||||
- [Showing commits count for specified year](#showing-commits-count-for-specified-year)
|
||||
- [Themes](#themes)
|
||||
- [Customization](#customization)
|
||||
- [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:
|
||||

|
||||
```
|
||||
|
||||
### Showing commits count for specified year
|
||||
|
||||
You can specify a year and fetch only the commits that were made in that year by passing `&commits_year=YYYY` to the parameter.
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
### Themes
|
||||
|
||||
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
|
||||
| `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` |
|
||||
| `number_format` | Switches between two available formats for displaying the card values `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` |
|
||||
| `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` |
|
||||
| `commits_year` | Filters and counts only commits made in the specified year | integer _(YYYY)_ | `<current year> (one year to date)`.
|
||||
|
||||
> [!NOTE]\
|
||||
> When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding.
|
||||
|
||||
+31
-8
@@ -10,7 +10,7 @@ import {
|
||||
kFormatter,
|
||||
measureText,
|
||||
} from "../common/utils.js";
|
||||
import { statCardLocales } from "../translations.js";
|
||||
import { statCardLocales, wakatimeCardLocales } from "../translations.js";
|
||||
|
||||
const CARD_MIN_WIDTH = 287;
|
||||
const CARD_DEFAULT_WIDTH = 287;
|
||||
@@ -187,6 +187,21 @@ const getStyles = ({
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the label for commits according to the selected options
|
||||
*
|
||||
* @param {boolean} include_all_commits Option to include all years
|
||||
* @param {number|undefined} commits_year Option to include only selected year
|
||||
* @param {I18n} i18n The I18n instance.
|
||||
* @returns {string} The label corresponding to the options.
|
||||
*/
|
||||
const getTotalCommitsYearLabel = (include_all_commits, commits_year, i18n) =>
|
||||
include_all_commits
|
||||
? ""
|
||||
: commits_year
|
||||
? ` (${commits_year})`
|
||||
: ` (${i18n.t("wakatimecard.lastyear")})`;
|
||||
|
||||
/**
|
||||
* @typedef {import('../fetchers/types').StatsData} StatsData
|
||||
* @typedef {import('./types').StatCardOptions} StatCardOptions
|
||||
@@ -222,6 +237,7 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
card_width,
|
||||
hide_rank = false,
|
||||
include_all_commits = false,
|
||||
commits_year,
|
||||
line_height = 25,
|
||||
title_color,
|
||||
ring_color,
|
||||
@@ -257,7 +273,10 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
const apostrophe = /s$/i.test(name.trim()) ? "" : "s";
|
||||
const i18n = new I18n({
|
||||
locale,
|
||||
translations: statCardLocales({ name, apostrophe }),
|
||||
translations: {
|
||||
...statCardLocales({ name, apostrophe }),
|
||||
...wakatimeCardLocales,
|
||||
},
|
||||
});
|
||||
|
||||
// Meta data for creating text nodes with createTextNode function
|
||||
@@ -271,9 +290,11 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
};
|
||||
STATS.commits = {
|
||||
icon: icons.commits,
|
||||
label: `${i18n.t("statcard.commits")}${
|
||||
include_all_commits ? "" : ` (${new Date().getFullYear()})`
|
||||
}`,
|
||||
label: `${i18n.t("statcard.commits")}${getTotalCommitsYearLabel(
|
||||
include_all_commits,
|
||||
commits_year,
|
||||
i18n,
|
||||
)}`,
|
||||
value: totalCommits,
|
||||
id: "commits",
|
||||
};
|
||||
@@ -515,9 +536,11 @@ const renderStatsCard = (stats, options = {}) => {
|
||||
.filter((key) => !hide.includes(key))
|
||||
.map((key) => {
|
||||
if (key === "commits") {
|
||||
return `${i18n.t("statcard.commits")} ${
|
||||
include_all_commits ? "" : `in ${new Date().getFullYear()}`
|
||||
} : ${STATS[key].value}`;
|
||||
return `${i18n.t("statcard.commits")} ${getTotalCommitsYearLabel(
|
||||
include_all_commits,
|
||||
commits_year,
|
||||
i18n,
|
||||
)} : ${STATS[key].value}`;
|
||||
}
|
||||
return `${STATS[key].label}: ${STATS[key].value}`;
|
||||
})
|
||||
|
||||
Vendored
+1
@@ -20,6 +20,7 @@ export type StatCardOptions = CommonOptions & {
|
||||
card_width: number;
|
||||
hide_rank: boolean;
|
||||
include_all_commits: boolean;
|
||||
commits_year: number;
|
||||
line_height: number | string;
|
||||
custom_title: string;
|
||||
disable_animations: boolean;
|
||||
|
||||
+12
-5
@@ -40,12 +40,14 @@ const GRAPHQL_REPOS_QUERY = `
|
||||
`;
|
||||
|
||||
const GRAPHQL_STATS_QUERY = `
|
||||
query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!) {
|
||||
query userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null) {
|
||||
user(login: $login) {
|
||||
name
|
||||
login
|
||||
contributionsCollection {
|
||||
commits: contributionsCollection (from: $startTime) {
|
||||
totalCommitContributions,
|
||||
}
|
||||
reviews: contributionsCollection {
|
||||
totalPullRequestReviewContributions
|
||||
}
|
||||
repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
|
||||
@@ -109,6 +111,7 @@ const fetcher = (variables, token) => {
|
||||
* @param {boolean} variables.includeMergedPullRequests Include merged pull requests.
|
||||
* @param {boolean} variables.includeDiscussions Include discussions.
|
||||
* @param {boolean} variables.includeDiscussionsAnswers Include discussions answers.
|
||||
* @param {string|undefined} variables.startTime Time to start the count of total commits.
|
||||
* @returns {Promise<AxiosResponse>} Axios response.
|
||||
*
|
||||
* @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 ({
|
||||
includeMergedPullRequests,
|
||||
includeDiscussions,
|
||||
includeDiscussionsAnswers,
|
||||
startTime,
|
||||
}) => {
|
||||
let stats;
|
||||
let hasNextPage = true;
|
||||
@@ -130,6 +134,7 @@ const statsFetcher = async ({
|
||||
includeMergedPullRequests,
|
||||
includeDiscussions,
|
||||
includeDiscussionsAnswers,
|
||||
startTime,
|
||||
};
|
||||
let res = await retryer(fetcher, variables);
|
||||
if (res.data.errors) {
|
||||
@@ -217,6 +222,7 @@ const totalCommitsFetcher = async (username) => {
|
||||
* @param {boolean} include_merged_pull_requests Include merged pull requests.
|
||||
* @param {boolean} include_discussions Include discussions.
|
||||
* @param {boolean} include_discussions_answers Include discussions answers.
|
||||
* @param {number|undefined} commits_year Year to count total commits
|
||||
* @returns {Promise<StatsData>} Stats data.
|
||||
*/
|
||||
const fetchStats = async (
|
||||
@@ -226,6 +232,7 @@ const fetchStats = async (
|
||||
include_merged_pull_requests = false,
|
||||
include_discussions = false,
|
||||
include_discussions_answers = false,
|
||||
commits_year,
|
||||
) => {
|
||||
if (!username) {
|
||||
throw new MissingParamError(["username"]);
|
||||
@@ -251,6 +258,7 @@ const fetchStats = async (
|
||||
includeMergedPullRequests: include_merged_pull_requests,
|
||||
includeDiscussions: include_discussions,
|
||||
includeDiscussionsAnswers: include_discussions_answers,
|
||||
startTime: commits_year ? `${commits_year}-01-01T00:00:00Z` : undefined,
|
||||
});
|
||||
|
||||
// Catch GraphQL errors.
|
||||
@@ -282,7 +290,7 @@ const fetchStats = async (
|
||||
if (include_all_commits) {
|
||||
stats.totalCommits = await totalCommitsFetcher(username);
|
||||
} else {
|
||||
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
|
||||
stats.totalCommits = user.commits.totalCommitContributions;
|
||||
}
|
||||
|
||||
stats.totalPRs = user.pullRequests.totalCount;
|
||||
@@ -291,8 +299,7 @@ const fetchStats = async (
|
||||
stats.mergedPRsPercentage =
|
||||
(user.mergedPullRequests.totalCount / user.pullRequests.totalCount) * 100;
|
||||
}
|
||||
stats.totalReviews =
|
||||
user.contributionsCollection.totalPullRequestReviewContributions;
|
||||
stats.totalReviews = user.reviews.totalPullRequestReviewContributions;
|
||||
stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount;
|
||||
if (include_discussions) {
|
||||
stats.totalDiscussionsStarted = user.repositoryDiscussions.totalCount;
|
||||
|
||||
+3
-1
@@ -38,8 +38,10 @@ const data_stats = {
|
||||
user: {
|
||||
name: stats.name,
|
||||
repositoriesContributedTo: { totalCount: stats.contributedTo },
|
||||
contributionsCollection: {
|
||||
commits: {
|
||||
totalCommitContributions: stats.totalCommits,
|
||||
},
|
||||
reviews: {
|
||||
totalPullRequestReviewContributions: stats.totalReviews,
|
||||
},
|
||||
pullRequests: { totalCount: stats.totalPRs },
|
||||
|
||||
@@ -24,8 +24,10 @@ const data_stats = {
|
||||
user: {
|
||||
name: stats.name,
|
||||
repositoriesContributedTo: { totalCount: stats.contributedTo },
|
||||
contributionsCollection: {
|
||||
commits: {
|
||||
totalCommitContributions: stats.totalCommits,
|
||||
},
|
||||
reviews: {
|
||||
totalPullRequestReviewContributions: stats.totalReviews,
|
||||
},
|
||||
pullRequests: { totalCount: stats.totalPRs },
|
||||
|
||||
@@ -11,8 +11,10 @@ const data_stats = {
|
||||
user: {
|
||||
name: "Anurag Hazra",
|
||||
repositoriesContributedTo: { totalCount: 61 },
|
||||
contributionsCollection: {
|
||||
commits: {
|
||||
totalCommitContributions: 100,
|
||||
},
|
||||
reviews: {
|
||||
totalPullRequestReviewContributions: 50,
|
||||
},
|
||||
pullRequests: { totalCount: 300 },
|
||||
@@ -38,6 +40,9 @@ const data_stats = {
|
||||
},
|
||||
};
|
||||
|
||||
const data_year2003 = JSON.parse(JSON.stringify(data_stats));
|
||||
data_year2003.data.user.commits.totalCommitContributions = 428;
|
||||
|
||||
const data_repo = {
|
||||
data: {
|
||||
user: {
|
||||
@@ -91,9 +96,18 @@ const mock = new MockAdapter(axios);
|
||||
beforeEach(() => {
|
||||
process.env.FETCH_MULTI_PAGE_STARS = "false"; // Set to `false` to fetch only one page of stars.
|
||||
mock.onPost("https://api.github.com/graphql").reply((cfg) => {
|
||||
let req = JSON.parse(cfg.data);
|
||||
|
||||
if (
|
||||
req.variables &&
|
||||
req.variables.startTime &&
|
||||
req.variables.startTime.startsWith("2003")
|
||||
) {
|
||||
return [200, data_year2003];
|
||||
}
|
||||
return [
|
||||
200,
|
||||
cfg.data.includes("contributionsCollection") ? data_stats : data_repo,
|
||||
req.query.includes("totalCommitContributions") ? data_stats : data_repo,
|
||||
];
|
||||
});
|
||||
});
|
||||
@@ -409,4 +423,42 @@ describe("Test fetchStats", () => {
|
||||
rank,
|
||||
});
|
||||
});
|
||||
|
||||
it("should get commits of provided year", async () => {
|
||||
let stats = await fetchStats(
|
||||
"anuraghazra",
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
2003,
|
||||
);
|
||||
|
||||
const rank = calculateRank({
|
||||
all_commits: false,
|
||||
commits: 428,
|
||||
prs: 300,
|
||||
reviews: 50,
|
||||
issues: 200,
|
||||
repos: 5,
|
||||
stars: 300,
|
||||
followers: 100,
|
||||
});
|
||||
|
||||
expect(stats).toStrictEqual({
|
||||
contributedTo: 61,
|
||||
name: "Anurag Hazra",
|
||||
totalCommits: 428,
|
||||
totalIssues: 200,
|
||||
totalPRs: 300,
|
||||
totalPRsMerged: 0,
|
||||
mergedPRsPercentage: 0,
|
||||
totalReviews: 50,
|
||||
totalStars: 300,
|
||||
totalDiscussionsStarted: 0,
|
||||
totalDiscussionsAnswered: 0,
|
||||
rank,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -387,7 +387,7 @@ describe("Test renderStatsCard", () => {
|
||||
document.querySelector(
|
||||
'g[transform="translate(0, 25)"]>.stagger>.stat.bold',
|
||||
).textContent,
|
||||
).toMatchInlineSnapshot(`"累计提交总数 (${new Date().getFullYear()}):"`);
|
||||
).toMatchInlineSnapshot(`"累计提交总数 (去年):"`);
|
||||
expect(
|
||||
document.querySelector(
|
||||
'g[transform="translate(0, 50)"]>.stagger>.stat.bold',
|
||||
|
||||
Reference in New Issue
Block a user