From 3ebccd85b7ad13cc88b18e768bcead078beebd59 Mon Sep 17 00:00:00 2001 From: lowlighter <22963968+lowlighter@users.noreply.github.com> Date: Sun, 9 Jan 2022 12:32:30 -0500 Subject: [PATCH] fix(plugins/isocalendar): algorithm improvements for date ranges --- source/plugins/isocalendar/index.mjs | 81 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/source/plugins/isocalendar/index.mjs b/source/plugins/isocalendar/index.mjs index 9e9403048c5..dcff35ae8e5 100644 --- a/source/plugins/isocalendar/index.mjs +++ b/source/plugins/isocalendar/index.mjs @@ -13,33 +13,22 @@ export default async function({login, data, graphql, q, imports, queries, accoun const now = new Date() const start = new Date(now) if (duration === "full-year") - start.setFullYear(now.getFullYear() - 1) + start.setUTCFullYear(now.getUTCFullYear() - 1) else - start.setHours(-24 * 180) + start.setUTCHours(-180 * 24) - //Compute padding to ensure last row is complete - const padding = new Date(start) - padding.setHours(-14 * 24) + //Ensure start day is a sunday, and that time is set to 00:00:00.000 + if (start.getUTCDay()) + start.setUTCHours(-start.getUTCDay() * 24) + start.setUTCMilliseconds(0) + start.setUTCSeconds(0) + start.setUTCMinutes(0) + start.setUTCHours(0) - //Retrieve contribution calendar from graphql api - console.debug(`metrics/compute/${login}/plugins > isocalendar > querying api`) - const calendar = {} - for (const [name, from, to] of [["padding", padding, start], ["weeks", start, now]]) { - console.debug(`metrics/compute/${login}/plugins > isocalendar > loading ${name} from "${from.toISOString()}" to "${to.toISOString()}"`) - const {user:{calendar:{contributionCalendar:{weeks}}}} = await graphql(queries.isocalendar.calendar({login, from:from.toISOString(), to:to.toISOString()})) - calendar[name] = weeks - } - - //Apply padding - console.debug(`metrics/compute/${login}/plugins > isocalendar > applying padding`) - const firstweek = calendar.weeks[0].contributionDays - const padded = calendar.padding.flatMap(({contributionDays}) => contributionDays).filter(({date}) => !firstweek.map(({date}) => date).includes(date)) - while (firstweek.length < 7) - firstweek.unshift(padded.pop()) - - //Compute the highest contributions in a day, streaks and average commits per day + //Compute contribution calendar, highest contributions in a day, streaks and average commits per day console.debug(`metrics/compute/${login}/plugins > isocalendar > computing stats`) - const {streak, max, average} = await statistics({login, data, graphql, queries}) + const calendar = {weeks:[]} + const {streak, max, average} = await statistics({login, graphql, queries, start, end:now, calendar}) const reference = Math.max(...calendar.weeks.flatMap(({contributionDays}) => contributionDays.map(({contributionCount}) => contributionCount))) //Compute SVG @@ -91,28 +80,38 @@ export default async function({login, data, graphql, q, imports, queries, accoun } /**Compute max and current streaks */ -async function statistics({login, data, graphql, queries}) { +async function statistics({login, graphql, queries, start, end, calendar}) { let average = 0, max = 0, streak = {max:0, current:0}, values = [] - const now = new Date() - for (let from = new Date(data.user.createdAt); from < now;) { - //Load contribution calendar + //Load contribution calendar + for (let from = new Date(start); from < end;) { + //Set date range let to = new Date(from) - to.setFullYear(to.getFullYear() + 1) - if (to > now) - to = now - console.debug(`metrics/compute/${login}/plugins > isocalendar > loading calendar from "${from.toISOString()}" to "${to.toISOString()}"`) - const {user:{calendar:{contributionCalendar:{weeks}}}} = await graphql(queries.isocalendar.calendar({login, from:from.toISOString(), to:to.toISOString()})) - from = to - //Compute streaks - for (const week of weeks) { - for (const day of week.contributionDays) { - values.push(day.contributionCount) - max = Math.max(max, day.contributionCount) - streak.current = day.contributionCount ? streak.current + 1 : 0 - streak.max = Math.max(streak.max, streak.current) - } + to.setUTCHours(+4 * 7 * 24) + if (to > end) + to = end + //Ensure that date ranges are not overlapping by setting it to previous day at 23:59:59.999 + const dto = new Date(to) + dto.setUTCHours(-1) + dto.setUTCMinutes(59) + dto.setUTCSeconds(59) + dto.setUTCMilliseconds(999) + //Fetch data from api + console.debug(`metrics/compute/${login}/plugins > isocalendar > loading calendar from "${from.toISOString()}" to "${dto.toISOString()}"`) + const {user:{calendar:{contributionCalendar:{weeks}}}} = await graphql(queries.isocalendar.calendar({login, from:from.toISOString(), to:dto.toISOString()})) + calendar.weeks.push(...weeks) + //Set next date range start + from = new Date(to) + } + //Compute streaks + for (const week of calendar.weeks) { + for (const day of week.contributionDays) { + values.push(day.contributionCount) + max = Math.max(max, day.contributionCount) + streak.current = day.contributionCount ? streak.current + 1 : 0 + streak.max = Math.max(streak.max, streak.current) } } + //Compute average average = (values.reduce((a, b) => a + b, 0) / values.length).toFixed(2).replace(/[.]0+$/, "") return {streak, max, average} } \ No newline at end of file