forked from mirrored/github-readme-stats
Compare commits
10
Commits
rank
...
anu/new-donolink
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
430c614c1d | ||
|
|
024a469797 | ||
|
|
894f2e2c5a | ||
|
|
cec5ee3462 | ||
|
|
fa4d7ee7a1 | ||
|
|
14fe4cf1db | ||
|
|
1e0183d202 | ||
|
|
98b047751e | ||
|
|
1a2a82c3be | ||
|
|
c301289f7d |
@@ -15,10 +15,15 @@ jobs:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
filter: "1772"
|
||||
label: false
|
||||
label: true
|
||||
dashboard: true
|
||||
dashboard_show_total_reactions: true
|
||||
top_issues: true
|
||||
top_bugs: true
|
||||
top_features: true
|
||||
top_pull_requests: true
|
||||
custom_pull_requests_label: themes
|
||||
top_custom_pull_requests_label: ":star: top themes"
|
||||
top_custom_pull_requests_label_description:
|
||||
The description used for the top custom pull requests.
|
||||
top_custom_pull_requests_label_colour: #A23599
|
||||
|
||||
@@ -44,6 +44,16 @@ export default async (req, res) => {
|
||||
return res.send(renderError("Something went wrong", "Locale not found"));
|
||||
}
|
||||
|
||||
if (
|
||||
layout !== undefined &&
|
||||
(typeof layout !== "string" ||
|
||||
!["compact", "normal", "donut", "donut-vertical", "pie"].includes(layout))
|
||||
) {
|
||||
return res.send(
|
||||
renderError("Something went wrong", "Incorrect layout input"),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const topLangs = await fetchTopLanguages(
|
||||
username,
|
||||
|
||||
+1
-2
@@ -28,7 +28,6 @@ export default async (req, res) => {
|
||||
langs_count,
|
||||
hide,
|
||||
api_domain,
|
||||
range,
|
||||
border_radius,
|
||||
border_color,
|
||||
} = req.query;
|
||||
@@ -40,7 +39,7 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = await fetchWakatimeStats({ username, api_domain, range });
|
||||
const stats = await fetchWakatimeStats({ username, api_domain });
|
||||
|
||||
let cacheSeconds = clampValue(
|
||||
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
|
||||
|
||||
@@ -69,12 +69,15 @@
|
||||
<img src="https://cfstatic.give.do/logo.png" alt="Give india logo" width="200" />
|
||||
</a>
|
||||
|
||||
Are you considering supporting the project by donating? Please DO NOT!!
|
||||
Are you considering supporting the project by donating to me? Please DO NOT!!
|
||||
|
||||
Instead, Help India fight the second deadly wave of COVID-19.
|
||||
Thousands of people are dying in India because of a lack of Oxygen & also COVID-related infrastructure.
|
||||
|
||||
Visit <https://indiafightscorona.giveindia.org> and make a small donation to help us fight COVID and overcome this crisis. A small donation goes a long way. :heart:
|
||||
<img src="https://cfstatic.give.do/910ede2a-7892-43fe-8c8a-dea45e96d950.webp" alt="Picture of Coromandel Express train tragedy" width="35%">
|
||||
|
||||
India just suffered one of the most devastating train accident and your help will be immensely valuable for the people who were effected by this tragedy.
|
||||
|
||||
Please visit [this link](https://give.do/fundraisers/stand-beside-the-victims-of-the-coromandel-express-train-tragedy-in-odisha-donate-now) and make a small donation to help the people in need. A small donation goes a long way. :heart:
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
@@ -292,7 +295,7 @@ You can provide multiple comma-separated values in the bg_color option to render
|
||||
- `hide_title` - _(boolean)_. Default: `false`.
|
||||
- `layout` - Switch between five available layouts `normal` & `compact` & `donut` & `donut-vertical` & `pie`. Default: `normal`.
|
||||
- `card_width` - Set the card's width manually _(number)_. Default `300`.
|
||||
- `langs_count` - Show more languages on the card, between 1-10 _(number)_. Default `5`.
|
||||
- `langs_count` - Show more languages on the card, between 1-10 _(number)_. Default: `5` for `normal` and `donut`, `6` for other layouts.
|
||||
- `exclude_repo` - Exclude specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
||||
- `custom_title` - Sets a custom title for the card _(string)_. Default `Most Used Languages`.
|
||||
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
||||
@@ -315,7 +318,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`.
|
||||
- `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`.
|
||||
- `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 +470,15 @@ You can use the `&hide_progress=true` option to hide the percentages and the pro
|
||||
|
||||
# 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.
|
||||
|
||||
```md
|
||||
[](https://github.com/anuraghazra/github-readme-stats)
|
||||
```
|
||||
|
||||
> **Note**:
|
||||
> Please be aware that we currently only show data from Wakatime profiles that are public.
|
||||
|
||||
### Demo
|
||||
|
||||
[](https://github.com/anuraghazra/github-readme-stats)
|
||||
|
||||
@@ -14,11 +14,16 @@ import { langCardLocales } from "../translations.js";
|
||||
|
||||
const DEFAULT_CARD_WIDTH = 300;
|
||||
const MIN_CARD_WIDTH = 280;
|
||||
const DEFAULT_LANGS_COUNT = 5;
|
||||
const DEFAULT_LANG_COLOR = "#858585";
|
||||
const CARD_PADDING = 25;
|
||||
const COMPACT_LAYOUT_BASE_HEIGHT = 90;
|
||||
|
||||
const NORMAL_LAYOUT_DEFAULT_LANGS_COUNT = 5;
|
||||
const COMPACT_LAYOUT_DEFAULT_LANGS_COUNT = 6;
|
||||
const DONUT_LAYOUT_DEFAULT_LANGS_COUNT = 5;
|
||||
const PIE_LAYOUT_DEFAULT_LANGS_COUNT = 6;
|
||||
const DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT = 6;
|
||||
|
||||
/**
|
||||
* @typedef {import("../fetchers/types").Lang} Lang
|
||||
*/
|
||||
@@ -668,6 +673,26 @@ const noLanguagesDataNode = ({ color, text, layout }) => {
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get default languages count for provided card layout.
|
||||
*
|
||||
* @param {import("./types").TopLangOptions["layout"] | undefined} layout Input layout string
|
||||
* @return {number} Default languages count for input layout
|
||||
*/
|
||||
const getDefaultLanguagesCountByLayout = (layout) => {
|
||||
if (layout === "compact") {
|
||||
return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
|
||||
} else if (layout === "donut") {
|
||||
return DONUT_LAYOUT_DEFAULT_LANGS_COUNT;
|
||||
} else if (layout === "donut-vertical") {
|
||||
return DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT;
|
||||
} else if (layout === "pie") {
|
||||
return PIE_LAYOUT_DEFAULT_LANGS_COUNT;
|
||||
} else {
|
||||
return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders card that display user's most frequently used programming languages.
|
||||
*
|
||||
@@ -689,7 +714,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
layout,
|
||||
custom_title,
|
||||
locale,
|
||||
langs_count = DEFAULT_LANGS_COUNT,
|
||||
langs_count = getDefaultLanguagesCountByLayout(layout),
|
||||
border_radius,
|
||||
border_color,
|
||||
disable_animations,
|
||||
@@ -746,7 +771,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
totalLanguageSize,
|
||||
hide_progress,
|
||||
);
|
||||
} else if (layout?.toLowerCase() === "donut") {
|
||||
} else if (layout === "donut") {
|
||||
height = calculateDonutLayoutHeight(langs.length);
|
||||
width = width + 50; // padding
|
||||
finalLayout = renderDonutLayout(langs, width, totalLanguageSize);
|
||||
@@ -837,4 +862,5 @@ export {
|
||||
trimTopLanguages,
|
||||
renderTopLanguages,
|
||||
MIN_CARD_WIDTH,
|
||||
getDefaultLanguagesCountByLayout,
|
||||
};
|
||||
|
||||
@@ -279,7 +279,11 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
: noCodingActivityNode({
|
||||
// @ts-ignore
|
||||
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({
|
||||
// @ts-ignore
|
||||
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,
|
||||
@@ -312,9 +320,20 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
}).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({
|
||||
customTitle: custom_title,
|
||||
defaultTitle: i18n.t("wakatimecard.title"),
|
||||
defaultTitle: titleText,
|
||||
width: 495,
|
||||
height,
|
||||
border_radius,
|
||||
|
||||
@@ -305,6 +305,7 @@ const SECONDARY_ERROR_MESSAGES = {
|
||||
"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",
|
||||
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 USER_NOT_FOUND = "USER_NOT_FOUND";
|
||||
static GRAPHQL_ERROR = "GRAPHQL_ERROR";
|
||||
static WAKATIME_ERROR = "WAKATIME_ERROR";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
const fetchWakatimeStats = async ({ username, api_domain, range }) => {
|
||||
const fetchWakatimeStats = async ({ username, api_domain }) => {
|
||||
if (!username) throw new MissingParamError(["username"]);
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`https://${
|
||||
api_domain ? api_domain.replace(/\/$/gi, "") : "wakatime.com"
|
||||
}/api/v1/users/${username}/stats/${
|
||||
range || "all_time"
|
||||
}?is_including_today=true`,
|
||||
}/api/v1/users/${username}/stats?is_including_today=true`,
|
||||
);
|
||||
|
||||
return data.data;
|
||||
} catch (err) {
|
||||
if (err.response.status < 200 || err.response.status > 299) {
|
||||
throw new Error(
|
||||
"Wakatime user not found, make sure you have a wakatime profile",
|
||||
throw new CustomError(
|
||||
`Could not resolve to a User with the login of '${username}'`,
|
||||
"WAKATIME_USER_NOT_FOUND",
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
|
||||
@@ -356,6 +356,22 @@ const wakatimeCardLocales = {
|
||||
vi: "Thống Kê Wakatime",
|
||||
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": {
|
||||
ar: "لا يوجد نشاط برمجي لهذا الأسبوع",
|
||||
cn: "本周没有编程活动",
|
||||
|
||||
@@ -123,7 +123,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
||||
y="0"
|
||||
class="header"
|
||||
data-testid="header"
|
||||
>Wakatime Stats</text>
|
||||
>Wakatime Stats (last 7 days)</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
@@ -303,7 +303,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout w
|
||||
y="0"
|
||||
class="header"
|
||||
data-testid="header"
|
||||
>Wakatime Stats</text>
|
||||
>Wakatime Stats (last 7 days)</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ const STATS_DATA = {
|
||||
totalIssues: 1,
|
||||
totalStars: 1,
|
||||
contributedTo: 1,
|
||||
rank: {
|
||||
level: "A+",
|
||||
score: 50.88831151384285,
|
||||
},
|
||||
rank: { level: "B", score: 98.50610674501908 },
|
||||
};
|
||||
|
||||
const LANGS_DATA = {
|
||||
@@ -54,7 +51,7 @@ const WAKATIME_DATA = {
|
||||
is_up_to_date: false,
|
||||
is_up_to_date_pending_future: false,
|
||||
percent_calculated: 0,
|
||||
range: "last_7_days",
|
||||
range: "all_time",
|
||||
status: "pending_update",
|
||||
timeout: 15,
|
||||
username: USER,
|
||||
|
||||
@@ -105,7 +105,7 @@ describe("Wakatime fetcher", () => {
|
||||
const username = "anuraghazra";
|
||||
mock
|
||||
.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);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
trimTopLanguages,
|
||||
renderTopLanguages,
|
||||
MIN_CARD_WIDTH,
|
||||
getDefaultLanguagesCountByLayout,
|
||||
} from "../src/cards/top-languages-card.js";
|
||||
|
||||
// adds special assertions like toHaveTextContent
|
||||
@@ -328,6 +329,15 @@ describe("Test renderTopLanguages helper functions", () => {
|
||||
totalLanguageSize: 300,
|
||||
});
|
||||
});
|
||||
|
||||
it("getDefaultLanguagesCountByLayout", () => {
|
||||
expect(getDefaultLanguagesCountByLayout("normal")).toStrictEqual(5);
|
||||
expect(getDefaultLanguagesCountByLayout(undefined)).toStrictEqual(5);
|
||||
expect(getDefaultLanguagesCountByLayout("compact")).toStrictEqual(6);
|
||||
expect(getDefaultLanguagesCountByLayout("donut")).toStrictEqual(5);
|
||||
expect(getDefaultLanguagesCountByLayout("donut-vertical")).toStrictEqual(6);
|
||||
expect(getDefaultLanguagesCountByLayout("pie")).toStrictEqual(6);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test renderTopLanguages", () => {
|
||||
@@ -797,7 +807,7 @@ describe("Test renderTopLanguages", () => {
|
||||
expect(document.querySelector("rect")).toHaveAttribute("rx", "4.5");
|
||||
});
|
||||
|
||||
it("should render langs with specified langs_count", async () => {
|
||||
it("should render langs with specified langs_count", () => {
|
||||
const options = {
|
||||
langs_count: 1,
|
||||
};
|
||||
@@ -807,7 +817,7 @@ describe("Test renderTopLanguages", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should render langs with specified langs_count even when hide is set", async () => {
|
||||
it("should render langs with specified langs_count even when hide is set", () => {
|
||||
const options = {
|
||||
hide: ["HTML"],
|
||||
langs_count: 2,
|
||||
@@ -817,4 +827,11 @@ describe("Test renderTopLanguages", () => {
|
||||
options.langs_count,
|
||||
);
|
||||
});
|
||||
|
||||
it('should show "No languages data." message instead of empty card when nothing to show', () => {
|
||||
document.body.innerHTML = renderTopLanguages({});
|
||||
expect(document.querySelector(".stat").textContent).toBe(
|
||||
"No languages data.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("Test Render Wakatime Card", () => {
|
||||
expect(
|
||||
document.querySelector('g[transform="translate(0, 0)"]>text.stat.bold')
|
||||
.textContent,
|
||||
).toBe("本周没有编程活动");
|
||||
).toBe("Wakatime 用户个人资料未公开");
|
||||
});
|
||||
|
||||
it("should render without rounding", () => {
|
||||
|
||||
+22
-1
@@ -123,7 +123,7 @@ describe("Test /api/top-langs", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should render error card on error", async () => {
|
||||
it("should render error card on user data fetch error", async () => {
|
||||
const req = {
|
||||
query: {
|
||||
username: "anuraghazra",
|
||||
@@ -140,4 +140,25 @@ describe("Test /api/top-langs", () => {
|
||||
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
|
||||
expect(res.send).toBeCalledWith(renderError(error.errors[0].message));
|
||||
});
|
||||
|
||||
it("should render error card on incorrect layout input", async () => {
|
||||
const req = {
|
||||
query: {
|
||||
username: "anuraghazra",
|
||||
layout: ["pie"],
|
||||
},
|
||||
};
|
||||
const res = {
|
||||
setHeader: jest.fn(),
|
||||
send: jest.fn(),
|
||||
};
|
||||
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);
|
||||
|
||||
await topLangs(req, res);
|
||||
|
||||
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
|
||||
expect(res.send).toBeCalledWith(
|
||||
renderError("Something went wrong", "Incorrect layout input"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user