-
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rm import fix improper li nesting break counts apparently
- Loading branch information
1 parent
307b094
commit 697a88f
Showing
2 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
export function daysToMilliseconds(days) { | ||
return days * 24 * 60 * 60 * 1000; | ||
} | ||
|
||
function identifyAge(date) { | ||
const millis = new Date() - date; | ||
if (millis < daysToMilliseconds(7)) { | ||
return "this week"; | ||
} else if (millis < daysToMilliseconds(7 * 4)) { | ||
return "this month"; | ||
} else { | ||
return "old"; | ||
} | ||
} | ||
|
||
// TODO: Pull these in from config. | ||
export const modules = [ | ||
"Module-User-Focused-Data", | ||
"Module-Structuring-And-Testing-Data", | ||
"Module-Data-Groups", | ||
"Module-Data-Flows", | ||
]; | ||
|
||
class PR { | ||
// status: one of: "Needs Review", "Reviewed", "Complete", "Closed", "Unknown" | ||
constructor(url, number, userName, userUrl, title, module, createdAge, updatedAge, status) { | ||
this.url = url; | ||
this.number = number; | ||
this.userName = userName; | ||
this.userUrl = userUrl; | ||
this.title = title; | ||
this.module = module; | ||
this.createdAge = createdAge; | ||
this.updatedAge = updatedAge; | ||
this.status = status; | ||
this.comments = []; | ||
} | ||
} | ||
|
||
class Comment { | ||
constructor(userName, isPrAuthor, createdAt) { | ||
this.userName = userName; | ||
this.isPrAuthor = isPrAuthor; | ||
this.createdAt = createdAt; | ||
} | ||
} | ||
|
||
function getStatus(state, labels) { | ||
// TODO: Check possibilities | ||
if (state !== "open") { | ||
return "Closed" | ||
} | ||
for (const possibleLabel of ["Needs Review", "Complete", "Reviewed"]) { | ||
if (labels.some((label) => label.name === possibleLabel)) { | ||
return possibleLabel; | ||
} | ||
} | ||
return "Unknown"; | ||
} | ||
|
||
export async function fetchPrs() { | ||
const prs = []; | ||
const responsePromises = []; | ||
for (const module of modules) { | ||
responsePromises.push(fetch(`https://github-issue-proxy.illicitonion.com/cached/120/repos/CodeYourFuture/${module}/pulls?state=all`).then((response) => response.json())); | ||
} | ||
const responsesByModule = await Promise.all(responsePromises); | ||
for (let i = 0; i < responsesByModule.length; i++) { | ||
const module = modules[i]; | ||
const responsePrs = responsesByModule[i]; | ||
for (const pr of responsePrs) { | ||
const status = getStatus(pr.state, pr.labels); | ||
const createdAt = new Date(Date.parse(pr["created_at"])); | ||
const updatedAt = new Date(Date.parse(pr["updated_at"])); | ||
|
||
prs.push(new PR(pr.html_url, pr.number, pr.user.login, pr.user.html_url, pr.title, module, identifyAge(createdAt), identifyAge(updatedAt), status)); | ||
} | ||
} | ||
return prs; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
const awaitingReviewByAge = {}; | ||
const prsByModule = {}; | ||
|
||
// 400/700/900 | ||
function badness(name, count) { | ||
if ((name === "old" || name == "this month") && count > 0) { | ||
return 900; | ||
} | ||
if (name === "this week" && count > 20) { | ||
return 900; | ||
} | ||
if (name === "this week" && count > 10) { | ||
return 700; | ||
} | ||
return 400; | ||
} | ||
|
||
const ageToEmoji = { | ||
"this week": "🟢", | ||
"this month": "🟠", | ||
old: "🔴", | ||
}; | ||
|
||
function computeStatusClass(awaitingReview) { | ||
const score = Math.max( | ||
...Object.entries(awaitingReview).map(([name, count]) => | ||
badness(name, count) | ||
) | ||
); | ||
if (score === 900) { | ||
return "is-bad"; | ||
} else if (score === 700) { | ||
return "is-medium"; | ||
} else { | ||
return "is-good"; | ||
} | ||
} | ||
|
||
async function onLoad() { | ||
for (const module of modules) { | ||
awaitingReviewByAge[module] = { | ||
"this week": 0, | ||
"this month": 0, | ||
old: 0, | ||
}; | ||
prsByModule[module] = []; | ||
} | ||
|
||
for (const pr of (await fetchPrs()).filter( | ||
(pr) => pr.status === "Needs Review" | ||
)) { | ||
awaitingReviewByAge[pr.module][pr.updatedAge]++; | ||
prsByModule[pr.module].push(pr); | ||
} | ||
for (const module of modules) { | ||
prsByModule[module].sort((l, r) => { | ||
if (l.updatedAge > r.updatedAge) { | ||
return 1; | ||
} else if (l.updatedAge < r.updatedAge) { | ||
return -1; | ||
} else { | ||
return l.number - r.number; | ||
} | ||
}); | ||
} | ||
|
||
document.querySelector("#pr-list").innerText = ""; | ||
|
||
for (const module of modules) { | ||
const awaitingReview = awaitingReviewByAge[module]; | ||
const totalPending = Object.values(awaitingReview).reduce( | ||
(acc, cur) => acc + cur, | ||
0 | ||
); | ||
|
||
const overviewCard = document | ||
.querySelector("template.overview-card") | ||
.content.cloneNode(true); | ||
overviewCard.querySelector( | ||
".module" | ||
).innerText = `${module} (${totalPending})`; | ||
for (const [age, count] of Object.entries(awaitingReview)) { | ||
const bucket = overviewCard.querySelector( | ||
`.age-bucket.${age.replaceAll(" ", "-")} .count` | ||
); | ||
if (bucket) bucket.innerText = count; | ||
if (bucket && badness(age, count) === 900) { | ||
bucket.classList.add("problem"); | ||
} | ||
} | ||
overviewCard | ||
.querySelector(".overview-card") | ||
.classList.add(computeStatusClass(awaitingReview)); | ||
document.querySelector("#overview").appendChild(overviewCard); | ||
|
||
if (totalPending) { | ||
const modulePrList = document | ||
.querySelector("template.pr-list") | ||
.content.cloneNode(true); | ||
modulePrList.querySelector( | ||
".module" | ||
).innerText = `${module} (${totalPending})`; | ||
for (const pr of prsByModule[module]) { | ||
const prInList = document | ||
.querySelector("template.pr-in-list") | ||
.content.cloneNode(true); | ||
|
||
prInList.querySelector(".emoji").innerText = ageToEmoji[pr.updatedAge]; | ||
|
||
const prLink = prInList.querySelector("a.pr-link"); | ||
prLink.href = pr.url; | ||
prLink.innerText = `${pr.title}`; | ||
|
||
const userLink = prInList.querySelector("a.user-link"); | ||
userLink.href = pr.userUrl; | ||
userLink.innerText = `${pr.userName}`; | ||
|
||
prInList.querySelector(".pr-number").innerText = pr.number; | ||
modulePrList.querySelector("ul.pr-list").appendChild(prInList); | ||
} | ||
document.querySelector("#pr-list").appendChild(modulePrList); | ||
} | ||
} | ||
} | ||
|
||
onLoad(); |