Commit 34f505f

Anton Medvedev <anton@medv.io>
2023-10-14 11:35:57
Add old-issue badges
1 parent a517067
src/all-badges/old-issue/old-issue-1.png
Binary file
src/all-badges/old-issue/old-issue-10.png
Binary file
src/all-badges/old-issue/old-issue-2.png
Binary file
src/all-badges/old-issue/old-issue-3.png
Binary file
src/all-badges/old-issue/old-issue-4.png
Binary file
src/all-badges/old-issue/old-issue-5.png
Binary file
src/all-badges/old-issue/old-issue-6.png
Binary file
src/all-badges/old-issue/old-issue-7.png
Binary file
src/all-badges/old-issue/old-issue-8.png
Binary file
src/all-badges/old-issue/old-issue-9.png
Binary file
src/all-badges/old-issue/old-issue.ts
@@ -0,0 +1,48 @@
+import { BadgePresenter, Present } from '../../badges.js'
+import { Issue } from '../../collect/collect.js'
+
+export default new (class implements BadgePresenter {
+  url = new URL(import.meta.url)
+  tiers = true
+  badges = [
+    'old-issue-1',
+    'old-issue-2',
+    'old-issue-3',
+    'old-issue-4',
+    'old-issue-5',
+    'old-issue-6',
+    'old-issue-7',
+    'old-issue-8',
+    'old-issue-9',
+    'old-issue-10',
+  ] as const
+  present: Present = (data, grant) => {
+    const buckets: { [years: number]: Issue[] } = {}
+
+    for (const issue of data.issues.sort(age)) {
+      if (!issue.closed) continue
+      const createdAt = new Date(issue.createdAt)
+      const closedAt = new Date(issue.closedAt)
+      let years = Math.floor(
+        (closedAt.getTime() - createdAt.getTime()) / 1000 / 60 / 60 / 24 / 365,
+      )
+      if (years > 10) years = 10
+      buckets[years] = buckets[years] || []
+      buckets[years].push(issue)
+    }
+
+    for (let years = 1; years <= 10; years++) {
+      if (!buckets[years]) continue
+      grant(
+        `old-issue-${years}` as (typeof this.badges)[number],
+        `I closed an issue that was open for ${years} years`,
+      )
+        .evidenceIssuesWithTitles(...buckets[years])
+        .tier(years)
+    }
+  }
+})()
+
+function age(a: Issue, b: Issue) {
+  return new Date(a.closedAt).getTime() - new Date(b.closedAt).getTime()
+}
src/all-badges/index.ts
@@ -16,4 +16,5 @@ export const allBadges = [
   await import('./covid-19/covid-19.js'),
   await import('./pr-collaboration/pr-collaboration.js'),
   await import('./public-keys/public-keys.js'),
+  await import('./old-issue/old-issue.js'),
 ] as const
src/collect/collect.ts
@@ -11,7 +11,7 @@ export type Data = {
   user: User
   repos: Repo[]
   pulls: Pull[]
-  issues: Issues[]
+  issues: Issue[]
 }
 export type User = UserQuery['user']
 export type Repo =
@@ -21,7 +21,7 @@ export type Repo =
 export type Commit =
   CommitsQuery['repository']['defaultBranchRef']['target']['history']['nodes'][0]
 export type Pull = PullsQuery['user']['pullRequests']['nodes'][0]
-export type Issues = IssuesQuery['user']['issues']['nodes'][0]
+export type Issue = IssuesQuery['user']['issues']['nodes'][0]
 
 export async function collect(
   octokit: Octokit,
src/badges.ts
@@ -1,6 +1,6 @@
 import { allBadges } from './all-badges/index.js'
-import { Commit, Data, Pull } from './collect/collect.js'
-import { expectType, linkCommit, linkPull } from './utils.js'
+import { Commit, Data, Issue, Pull } from './collect/collect.js'
+import { expectType, linkCommit, linkIssue, linkPull } from './utils.js'
 
 for (const {
   default: { badges },
@@ -91,4 +91,12 @@ class Evidence {
     )
     return this
   }
+
+  evidenceIssuesWithTitles(...issues: Issue[]) {
+    this.evidence(
+      'Issues:\n\n' +
+        issues.map((x) => `- ${linkIssue(x)}: ${x.title}`).join('\n'),
+    )
+    return this
+  }
 }
src/utils.ts
@@ -1,7 +1,7 @@
 import fs from 'node:fs/promises'
 import path from 'node:path'
 import { Octokit } from 'octokit'
-import { Commit, Pull } from './collect/collect.js'
+import { Commit, Issue, Pull } from './collect/collect.js'
 import { Badge } from './badges.js'
 
 export function linkCommit(commit: Commit): string {
@@ -14,6 +14,10 @@ export function linkPull(pull: Pull): string {
   return `<a href="https://github.com/${pull.repository.owner.login}/${pull.repository.name}/pull/${pull.number}">#${pull.number}</a>`
 }
 
+export function linkIssue(issue: Issue): string {
+  return `<a href="https://github.com/${issue.repository.owner.login}/${issue.repository.name}/issues/${issue.number}">#${issue.number}</a>`
+}
+
 export function quoteAttr(s: string) {
   return s
     .replace(/&/g, '&amp;')