-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ITP code review tracking dashboard
- Loading branch information
1 parent
08233ac
commit 80611dd
Showing
3 changed files
with
216 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,18 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<link href="https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Raleway:wght@300;600;800;900&family=Lato:wght@500;800&display=swap" rel="stylesheet" media="all" onload="this.media="all""> | ||
<meta charset="UTF-8"> | ||
<style type="text/css"> | ||
body { | ||
font-family: Raleway, sans-serif; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>Tracking for programme progress</h1> | ||
<ul> | ||
<li><a href="itd-prs">ITD code reviews</a></li> | ||
</ul> | ||
</body> | ||
</html> |
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,66 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>ITP PRs needing review</title> | ||
<link href="https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Raleway:wght@300;600;800;900&family=Lato:wght@500;800&display=swap" rel="stylesheet" media="all" onload="this.media="all""> | ||
<meta charset="UTF-8"> | ||
<style type="text/css"> | ||
body { | ||
font-family: Raleway, sans-serif; | ||
} | ||
#overview { | ||
display: grid; | ||
grid-template-columns: repeat(4, 1fr); | ||
gap: 40px; | ||
} | ||
.overview-card { | ||
padding: 0px 5px 20px 5px; | ||
text-align: center; | ||
} | ||
.age-container { | ||
display: grid; | ||
grid-template-columns: repeat(3, 1fr); | ||
} | ||
.age-bucket .count { | ||
font-weight: bolder; | ||
} | ||
.status-bad { | ||
background-color: red; | ||
} | ||
.status-medium { | ||
background-color: orange; | ||
} | ||
.status-good { | ||
background-color: green; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<h1>ITP PRs needing review</h1> | ||
<div id="overview"></div> | ||
<div id="pr-list">Loading...</div> | ||
<script type="text/javascript" src="index.js"></script> | ||
|
||
<template class="overview-card"> | ||
<div class="overview-card"> | ||
<h3 class="module"></h2> | ||
<div class="summary-heading"> | ||
PRs needing review from: | ||
<div class="age-container"> | ||
<div class="age-bucket this-week">This week<br /><span class="count"></span></div> | ||
<div class="age-bucket this-month">This month<br /><span class="count"></span></div> | ||
<div class="age-bucket old">Older<br /><span class="count"></span></div> | ||
</div> | ||
<div class="summary-values"></div> | ||
</div> | ||
</template> | ||
<template class="pr-list"> | ||
<h2 class="module"></h2> | ||
<ul class="pr-list"> | ||
</ul> | ||
</template> | ||
<template class="pr-in-list"> | ||
<li><span class="emoji"></span> <a class="pr-link"></a> (<a class="user-link"></a> - #<span class="pr-number"></span>)</li> | ||
</template> | ||
</body> | ||
</html> |
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,132 @@ | ||
const modules = [ | ||
"Module-User-Focused-Data", | ||
"Module-Structuring-And-Testing-Data", | ||
"Module-Data-Groups", | ||
"Module-Data-Flows", | ||
]; | ||
const ageToEmoji = { | ||
"this week": "🟢", | ||
"this month": "🟠", | ||
"old": "🔴", | ||
}; | ||
const now = new Date(); | ||
|
||
class PR { | ||
constructor(url, number, userName, userUrl, title, module, createdAge, updatedAge) { | ||
this.url = url; | ||
this.number = number; | ||
this.userName = userName; | ||
this.userUrl = userUrl; | ||
this.title = title; | ||
this.module = module; | ||
this.createdAge = createdAge; | ||
this.updatedAge = updatedAge; | ||
} | ||
} | ||
|
||
const awaitingReviewByAge = {}; | ||
const prsByModule = {}; | ||
|
||
function computeStatusClass(awaitingReview) { | ||
if (awaitingReview["old"] > 0 || awaitingReview["this month"] > 0 || awaitingReview["this week"] > 20) { | ||
return "status-bad"; | ||
} else if (awaitingReview["this week"] > 10) { | ||
return "status-medium"; | ||
} else { | ||
return "status-good"; | ||
} | ||
} | ||
|
||
function daysToMilliseconds(days) { | ||
return days * 24 * 60 * 60 * 1000; | ||
} | ||
|
||
function identifyAge(date) { | ||
const millis = now - date; | ||
if (millis < daysToMilliseconds(7)) { | ||
return "this week"; | ||
} else if (millis < daysToMilliseconds(7 * 4)) { | ||
return "this month"; | ||
} else { | ||
return "old"; | ||
} | ||
} | ||
|
||
async function onLoad() { | ||
for (const module of modules) { | ||
awaitingReviewByAge[module] = { | ||
"this week": 0, | ||
"this month": 0, | ||
"old": 0, | ||
}; | ||
prsByModule[module] = []; | ||
|
||
let response = await fetch(`https://github-issue-proxy.illicitonion.com/cached/1/repos/CodeYourFuture/${module}/pulls`); | ||
let prs = await response.json(); | ||
|
||
for (const pr of prs) { | ||
if (pr.state !== "open") { | ||
continue; | ||
} | ||
const needsReview = pr.labels.some((label) => label.name === "Needs Review"); | ||
if (!needsReview) { | ||
continue; | ||
} | ||
const createdAt = new Date(Date.parse(pr["created_at"])); | ||
const updatedAt = new Date(Date.parse(pr["updated_at"])); | ||
|
||
const prObj = new PR(pr.html_url, pr.number, pr.user.login, pr.user.html_url, pr.title, module, identifyAge(createdAt), identifyAge(updatedAt)); | ||
awaitingReviewByAge[module][prObj.updatedAge]++; | ||
prsByModule[module].push(prObj); | ||
} | ||
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)) { | ||
overviewCard.querySelector(`.age-bucket.${age.replaceAll(" ", "-")} .count`).innerText = count; | ||
} | ||
//`${} / ${awaitingReview["this month"]} / ${awaitingReview["old"]}`; | ||
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.appendChild(prInList); | ||
} | ||
document.querySelector("#pr-list").appendChild(modulePrList); | ||
} | ||
} | ||
} | ||
|
||
onLoad(); |