fix: Make WakaTime card compatible with new API (#2707)

* fix: Make WakaTime card compatible with new API

This commit makes sure that the WakaTime card works with the new
WakaTime API. See https://github.com/anuraghazra/github-readme-stats/issues/2698
for more information.

* fix: fix chinese simplified translations

* fix: improve WakaTime range order

* test: fix WakaTime tests

* refactor: remove WakaTime range loop

* refactor: remove redundant WakaTime call

* test: fix e2e tests

Co-authored-by: Hakula Chen <i@hakula.xyz>

---------

Co-authored-by: Hakula Chen <i@hakula.xyz>
This commit is contained in:
Rick Staa 2023-06-02 11:07:59 +02:00 committed by GitHub
parent e0b3d833b0
commit c301289f7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 57 additions and 22 deletions

View file

@ -28,7 +28,6 @@ export default async (req, res) => {
langs_count, langs_count,
hide, hide,
api_domain, api_domain,
range,
border_radius, border_radius,
border_color, border_color,
} = req.query; } = req.query;
@ -40,7 +39,7 @@ export default async (req, res) => {
} }
try { try {
const stats = await fetchWakatimeStats({ username, api_domain, range }); const stats = await fetchWakatimeStats({ username, api_domain });
let cacheSeconds = clampValue( let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10), parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),

View file

@ -315,7 +315,6 @@ You can provide multiple comma-separated values in the bg_color option to render
- `layout` - Switch between two available layouts `default` & `compact`. Default `default`. - `layout` - Switch between two available layouts `default` & `compact`. Default `default`.
- `langs_count` - Limit the number of languages on the card, defaults to all reported languages _(number)_. - `langs_count` - Limit the number of languages on the card, defaults to all reported languages _(number)_.
- `api_domain` - Set a custom API domain for the card, e.g. to use services like [Hakatime](https://github.com/mujx/hakatime) or [Wakapi](https://github.com/muety/wakapi) _(string)_. Default `Waka API`. - `api_domain` - Set a custom API domain for the card, e.g. to use services like [Hakatime](https://github.com/mujx/hakatime) or [Wakapi](https://github.com/muety/wakapi) _(string)_. Default `Waka API`.
- `range` Request a range different from your WakaTime default, e.g. `last_7_days`. See [WakaTime API docs](https://wakatime.com/developers#stats) for a list of available options. _(YYYY-MM, last_7_days, last_30_days, last_6_months, last_year, or all_time)_. Default `all_time`.
* * * * * *
@ -468,15 +467,15 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro
# Wakatime Week Stats # Wakatime Week Stats
> **Warning**
> Please be aware that we currently only show data from Wakatime profiles that are public. You therefore have to make sure that **BOTH** `Display code time publicly` and `Display languages, editors, os, categories publicly` are enabled.
Change the `?username=` value to your [Wakatime](https://wakatime.com) username. Change the `?username=` value to your [Wakatime](https://wakatime.com) username.
```md ```md
[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) [![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats)
``` ```
> **Note**:
> Please be aware that we currently only show data from Wakatime profiles that are public.
### Demo ### Demo
[![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats) [![Harlok's wakatime stats](https://github-readme-stats.vercel.app/api/wakatime?username=Harlok)](https://github.com/anuraghazra/github-readme-stats)

View file

@ -279,7 +279,11 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
: noCodingActivityNode({ : noCodingActivityNode({
// @ts-ignore // @ts-ignore
color: textColor, color: textColor,
text: i18n.t("wakatimecard.nocodingactivity"), text: !stats.is_coding_activity_visible
? i18n.t("wakatimecard.notpublic")
: stats.is_other_usage_visible
? i18n.t("wakatimecard.nocodingactivity")
: i18n.t("wakatimecard.nocodedetails"),
}) })
} }
`; `;
@ -304,7 +308,11 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
noCodingActivityNode({ noCodingActivityNode({
// @ts-ignore // @ts-ignore
color: textColor, color: textColor,
text: i18n.t("wakatimecard.nocodingactivity"), text: !stats.is_coding_activity_visible
? i18n.t("wakatimecard.notpublic")
: stats.is_other_usage_visible
? i18n.t("wakatimecard.nocodingactivity")
: i18n.t("wakatimecard.nocodedetails"),
}), }),
], ],
gap: lheight, gap: lheight,
@ -312,9 +320,20 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
}).join(""); }).join("");
} }
// Get title range text
let titleText = i18n.t("wakatimecard.title");
switch (stats.range) {
case "last_7_days":
titleText += ` (${i18n.t("wakatimecard.last7days")})`;
break;
case "last_year":
titleText += ` (${i18n.t("wakatimecard.lastyear")})`;
break;
}
const card = new Card({ const card = new Card({
customTitle: custom_title, customTitle: custom_title,
defaultTitle: i18n.t("wakatimecard.title"), defaultTitle: titleText,
width: 495, width: 495,
height, height,
border_radius, border_radius,

View file

@ -305,6 +305,7 @@ const SECONDARY_ERROR_MESSAGES = {
"Please add an env variable called PAT_1 with your github token in vercel", "Please add an env variable called PAT_1 with your github token in vercel",
USER_NOT_FOUND: "Make sure the provided username is not an organization", USER_NOT_FOUND: "Make sure the provided username is not an organization",
GRAPHQL_ERROR: "Please try again later", GRAPHQL_ERROR: "Please try again later",
WAKATIME_USER_NOT_FOUND: "Make sure you have a public WakaTime profile",
}; };
/** /**
@ -324,6 +325,7 @@ class CustomError extends Error {
static MAX_RETRY = "MAX_RETRY"; static MAX_RETRY = "MAX_RETRY";
static USER_NOT_FOUND = "USER_NOT_FOUND"; static USER_NOT_FOUND = "USER_NOT_FOUND";
static GRAPHQL_ERROR = "GRAPHQL_ERROR"; static GRAPHQL_ERROR = "GRAPHQL_ERROR";
static WAKATIME_ERROR = "WAKATIME_ERROR";
} }
/** /**

View file

@ -1,29 +1,29 @@
import axios from "axios"; import axios from "axios";
import { MissingParamError } from "../common/utils.js"; import { CustomError, MissingParamError } from "../common/utils.js";
import { I18n } from "../common/I18n.js";
/** /**
* WakaTime data fetcher. * WakaTime data fetcher.
* *
* @param {{username: string, api_domain: string, range: string}} props Fetcher props. * @param {{username: string, api_domain: string }} props Fetcher props.
* @returns {Promise<WakaTimeData>} WakaTime data response. * @returns {Promise<WakaTimeData>} WakaTime data response.
*/ */
const fetchWakatimeStats = async ({ username, api_domain, range }) => { const fetchWakatimeStats = async ({ username, api_domain }) => {
if (!username) throw new MissingParamError(["username"]); if (!username) throw new MissingParamError(["username"]);
try { try {
const { data } = await axios.get( const { data } = await axios.get(
`https://${ `https://${
api_domain ? api_domain.replace(/\/$/gi, "") : "wakatime.com" api_domain ? api_domain.replace(/\/$/gi, "") : "wakatime.com"
}/api/v1/users/${username}/stats/${ }/api/v1/users/${username}/stats?is_including_today=true`,
range || "all_time"
}?is_including_today=true`,
); );
return data.data; return data.data;
} catch (err) { } catch (err) {
if (err.response.status < 200 || err.response.status > 299) { if (err.response.status < 200 || err.response.status > 299) {
throw new Error( throw new CustomError(
"Wakatime user not found, make sure you have a wakatime profile", `Could not resolve to a User with the login of '${username}'`,
"WAKATIME_USER_NOT_FOUND",
); );
} }
throw err; throw err;

View file

@ -356,6 +356,22 @@ const wakatimeCardLocales = {
vi: "Thống Kê Wakatime", vi: "Thống Kê Wakatime",
se: "Wakatime statistik", se: "Wakatime statistik",
}, },
"wakatimecard.lastyear": {
en: "last year",
cn: "去年",
},
"wakatimecard.last7days": {
en: "last 7 days",
cn: "最近 7 天",
},
"wakatimecard.notpublic": {
en: "Wakatime user profile not public",
cn: "Wakatime 用户个人资料未公开",
},
"wakatimecard.nocodedetails": {
en: "User doesn't publicly share detailed code statistics",
cn: "用户不公开分享详细的代码统计信息",
},
"wakatimecard.nocodingactivity": { "wakatimecard.nocodingactivity": {
ar: "لا يوجد نشاط برمجي لهذا الأسبوع", ar: "لا يوجد نشاط برمجي لهذا الأسبوع",
cn: "本周没有编程活动", cn: "本周没有编程活动",

View file

@ -123,7 +123,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
y="0" y="0"
class="header" class="header"
data-testid="header" data-testid="header"
>Wakatime Stats</text> >Wakatime Stats (last 7 days)</text>
</g> </g>
</g> </g>
@ -303,7 +303,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout w
y="0" y="0"
class="header" class="header"
data-testid="header" data-testid="header"
>Wakatime Stats</text> >Wakatime Stats (last 7 days)</text>
</g> </g>
</g> </g>

View file

@ -54,7 +54,7 @@ const WAKATIME_DATA = {
is_up_to_date: false, is_up_to_date: false,
is_up_to_date_pending_future: false, is_up_to_date_pending_future: false,
percent_calculated: 0, percent_calculated: 0,
range: "last_7_days", range: "all_time",
status: "pending_update", status: "pending_update",
timeout: 15, timeout: 15,
username: USER, username: USER,

View file

@ -105,7 +105,7 @@ describe("Wakatime fetcher", () => {
const username = "anuraghazra"; const username = "anuraghazra";
mock mock
.onGet( .onGet(
`https://wakatime.com/api/v1/users/${username}/stats/all_time?is_including_today=true`, `https://wakatime.com/api/v1/users/${username}/stats?is_including_today=true`,
) )
.reply(200, wakaTimeData); .reply(200, wakaTimeData);

View file

@ -43,7 +43,7 @@ describe("Test Render Wakatime Card", () => {
expect( expect(
document.querySelector('g[transform="translate(0, 0)"]>text.stat.bold') document.querySelector('g[transform="translate(0, 0)"]>text.stat.bold')
.textContent, .textContent,
).toBe("本周没有编程活动"); ).toBe("Wakatime 用户个人资料未公开");
}); });
it("should render without rounding", () => { it("should render without rounding", () => {