Last active
May 31, 2023 16:39
-
-
Save darcien/b393c7939e9ea14f8f428e089feb66bf to your computer and use it in GitHub Desktop.
script to visualize recent pr line diff counts for internal jokes
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
#!/usr/bin/env -S deno run | |
import { sortBy } from "https://deno.land/std@0.186.0/collections/sort_by.ts"; | |
import { groupBy } from "https://deno.land/std@0.186.0/collections/group_by.ts"; | |
import { reduceGroups } from "https://deno.land/std@0.186.0/collections/reduce_groups.ts"; | |
import { plot } from "https://deno.land/x/chart@1.1/mod.ts"; | |
// Using JSON from this gh --json output | |
// gh pr list --json title,additions,deletions,changedFiles,author,number --search "uiv2 in:title" --state=all --limit 300 | |
type Pr = { | |
additions: number; | |
author: { | |
id: string; | |
is_bot: boolean; | |
login: string; | |
name: string; | |
}; | |
changedFiles: number; | |
deletions: number; | |
number: number; | |
title: string; | |
}; | |
// Accepting JSON input from stdin | |
const decoder = new TextDecoder(); | |
const chunks = []; | |
for await (const chunk of Deno.stdin.readable) { | |
chunks.push(decoder.decode(chunk)); | |
} | |
const json = JSON.parse(chunks.join("")); | |
const rGhPrs = json as Array<Pr>; | |
const ghPrs = rGhPrs.filter((pr) => !pr.author.is_bot); | |
// This might need a patched nerdfonts | |
const folderIcon = ""; | |
function formatPosition(position: number) { | |
let medal; | |
switch (position) { | |
case 1: | |
medal = "🥇"; | |
break; | |
case 2: | |
medal = "🥈"; | |
break; | |
case 3: | |
medal = "🥉"; | |
break; | |
} | |
return medal; | |
} | |
function formatPr(pr: Pr, position: number) { | |
return [ | |
`${formatPosition(position)} ${pr.author.name}`, | |
`${pr.title} #${pr.number}`, | |
`%c+${pr.additions} %c-${pr.deletions} %c${folderIcon}${pr.changedFiles}`, | |
].join("\n"); | |
} | |
type AuthorMeta = { | |
totalAdditions: number; | |
totalDeletions: number; | |
totalAddDelete: number; | |
totalChangedFiles: number; | |
totalPrs: number; | |
author: Pr["author"]; | |
}; | |
function formatMeta( | |
meta: AuthorMeta, | |
position: number, | |
{ | |
showAdditions, | |
showChangedFiles, | |
showPrCount, | |
showDeletions, | |
}: Partial<{ | |
showAdditions: boolean; | |
showDeletions: boolean; | |
showPrCount: boolean; | |
showChangedFiles: boolean; | |
}> = {} | |
) { | |
const add = showAdditions ? `+${meta.totalAdditions}` : ""; | |
const del = showDeletions ? `-${meta.totalDeletions}` : ""; | |
const cha = showChangedFiles ? `${folderIcon}${meta.totalChangedFiles}` : ""; | |
return [ | |
`${formatPosition(position)} ${meta.author.name}`, | |
showPrCount ? `PR count: ${meta.totalPrs}` : null, | |
`%c${add} %c${del} %c${cha}`, | |
] | |
.filter(Boolean) | |
.join("\n"); | |
} | |
const prsByAuthor = groupBy(ghPrs, (pr) => pr.author.id) as Record< | |
string, | |
Array<Pr> | |
>; | |
const metaByAuthor = reduceGroups( | |
prsByAuthor, | |
(meta, pr) => ({ | |
totalAdditions: meta.totalAdditions + pr.additions, | |
totalDeletions: meta.totalDeletions + pr.deletions, | |
totalAddDelete: meta.totalAddDelete + pr.additions + pr.deletions, | |
totalChangedFiles: meta.totalChangedFiles + pr.changedFiles, | |
totalPrs: meta.totalPrs + 1, | |
author: pr.author, | |
}), | |
{ | |
totalAdditions: 0, | |
totalDeletions: 0, | |
totalAddDelete: 0, | |
totalChangedFiles: 0, | |
totalPrs: 0, | |
author: {}, | |
} as AuthorMeta | |
); | |
const metas = Object.values(metaByAuthor); | |
const [ | |
authorAdditions, | |
authorDeletions, | |
authorAddDelete, | |
authorTotalPrs, | |
authorChangedFiles, | |
] = [ | |
sortBy(metas, (meta) => meta.totalAdditions), | |
sortBy(metas, (meta) => meta.totalDeletions), | |
sortBy(metas, (meta) => meta.totalAddDelete), | |
sortBy(metas, (meta) => meta.totalPrs), | |
sortBy(metas, (meta) => meta.totalChangedFiles), | |
]; | |
const [prAdditions, prDeletions, prAddDelete, prChangedFiles] = [ | |
sortBy(ghPrs, (pr) => pr.additions), | |
sortBy(ghPrs, (pr) => pr.deletions), | |
sortBy(ghPrs, (pr) => pr.additions + pr.deletions), | |
sortBy(ghPrs, (pr) => pr.changedFiles), | |
]; | |
const [top3Additions, top3Deletions, top3AddDelete, top3ChangedFiles] = [ | |
prAdditions.slice(-3).reverse(), | |
prDeletions.slice(-3).reverse(), | |
prAddDelete.slice(-3).reverse(), | |
prChangedFiles.slice(-3).reverse(), | |
]; | |
console.log("\n###\n"); | |
console.log("ADDITIONS AND DELETIONS PLOT\n"); | |
console.log( | |
plot( | |
[ | |
prAdditions.map((pr) => pr.additions), | |
prDeletions.map((pr) => pr.deletions), | |
], | |
{ | |
colors: ["green", "red"], | |
height: 24, | |
} | |
) | |
); | |
console.log("\n###\n"); | |
console.log("CHANGED FILES PLOT\n"); | |
console.log( | |
plot([prChangedFiles.map((pr) => pr.changedFiles)], { | |
colors: ["magenta"], | |
height: 24, | |
}) | |
); | |
function formatPrRankingAndLog(arr: typeof ghPrs) { | |
arr | |
.map((pr, index) => formatPr(pr, index + 1)) | |
.map((formatted) => | |
console.log( | |
formatted + "\n", | |
"color: green", | |
"color: red", | |
"color: yellow" | |
) | |
); | |
} | |
function formatAuthorRankingAndLog( | |
arr: typeof metas, | |
config: Partial<{ | |
showAdditions: boolean; | |
showDeletions: boolean; | |
showPrCount: boolean; | |
showChangedFiles: boolean; | |
}> | |
) { | |
arr | |
.map((meta, index) => formatMeta(meta, index + 1, config)) | |
.map((formatted) => | |
console.log( | |
formatted + "\n", | |
"color: green", | |
"color: red", | |
"color: yellow" | |
) | |
); | |
} | |
console.log("\n###\n"); | |
console.log("TOP 3 ADDITIONS BY PR!\n"); | |
formatPrRankingAndLog(top3Additions); | |
console.log("\n###\n"); | |
console.log("TOP 3 DELETIONS BY PR!\n"); | |
formatPrRankingAndLog(top3Deletions); | |
console.log("\n###\n"); | |
console.log("TOP 3 ADDITIONS + DELETIONS BY PR!\n"); | |
formatPrRankingAndLog(top3AddDelete); | |
console.log("\n###\n"); | |
console.log("TOP 3 CHANGED FILES BY PR!\n"); | |
formatPrRankingAndLog(top3ChangedFiles); | |
console.log("\n###\n"); | |
console.log("TOP 3 TOTAL ADDITIONS!\n"); | |
formatAuthorRankingAndLog(authorAdditions.slice(-3).reverse(), { | |
showAdditions: true, | |
}); | |
console.log("\n###\n"); | |
console.log("TOP 3 TOTAL DELETIONS!\n"); | |
formatAuthorRankingAndLog(authorDeletions.slice(-3).reverse(), { | |
showDeletions: true, | |
}); | |
console.log("\n###\n"); | |
console.log("TOP 3 TOTAL ADDITIONS + DELETIONS!\n"); | |
formatAuthorRankingAndLog(authorAddDelete.slice(-3).reverse(), { | |
showAdditions: true, | |
showDeletions: true, | |
}); | |
console.log("\n###\n"); | |
console.log("TOP 3 TOTAL PRS!\n"); | |
formatAuthorRankingAndLog(authorTotalPrs.slice(-3).reverse(), { | |
showPrCount: true, | |
}); | |
console.log("\n###\n"); | |
console.log("TOP 3 TOTAL CHANGED FILES!\n"); | |
formatAuthorRankingAndLog(authorChangedFiles.slice(-3).reverse(), { | |
showChangedFiles: true, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment