Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugins/isocalendar): algorithm improvements for date ranges #759

Merged
merged 1 commit into from
Jan 9, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 40 additions & 41 deletions source/plugins/isocalendar/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
}