| 1 | const STATUS_KEYS = ["open", "resolved", "ignored", "other"]; |
| 2 | const AGE_KEYS = ["under30d", "days30to59d", "days60plus", "invalid"]; |
| 3 | |
| 4 | function emptyCounts(keys) { |
| 5 | return Object.fromEntries(keys.map((key) => [key, 0])); |
| 6 | } |
| 7 | |
| 8 | function statusKey(value) { |
| 9 | return value === "open" || value === "resolved" || value === "ignored" ? value : "other"; |
| 10 | } |
| 11 | |
| 12 | function ageKey(value, now) { |
| 13 | const age = now.getTime() - new Date(value == null ? "" : String(value)).getTime(); |
| 14 | if (!Number.isFinite(age)) return "invalid"; |
| 15 | if (age < 30 * 86400_000) return "under30d"; |
| 16 | return age < 60 * 86400_000 ? "days30to59d" : "days60plus"; |
| 17 | } |
| 18 | |
| 19 | export function createMigrationCapacityAssessment() { |
| 20 | return { |
| 21 | statusTotals: emptyCounts(STATUS_KEYS), |
| 22 | ageByStatus: Object.fromEntries(STATUS_KEYS.map((status) => [status, emptyCounts(AGE_KEYS)])), |
| 23 | }; |
| 24 | } |
| 25 | |
| 26 | export function accumulateMigrationCapacityAssessment(summary, rows, now = new Date()) { |
| 27 | for (const row of rows) { |
| 28 | const status = statusKey(row.status); |
| 29 | const age = ageKey(row.last_seen, now); |
| 30 | summary.statusTotals[status]++; |
| 31 | summary.ageByStatus[status][age]++; |
| 32 | } |
| 33 | return summary; |
| 34 | } |
| 35 | |
| 36 | export function finalizeMigrationCapacityAssessment(summary) { |
| 37 | const { open, resolved, ignored, other } = summary.ageByStatus; |
| 38 | return { |
| 39 | ...summary, |
| 40 | activeReasons: { |
| 41 | open: summary.statusTotals.open, |
| 42 | otherStatus: summary.statusTotals.other, |
| 43 | recentResolvedOrIgnored: resolved.under30d + ignored.under30d, |
| 44 | invalidResolvedOrIgnored: resolved.invalid + ignored.invalid, |
| 45 | }, |
| 46 | automaticRetention: { |
| 47 | compacted: resolved.days30to59d + ignored.days30to59d, |
| 48 | archived: resolved.days60plus + ignored.days60plus, |
| 49 | }, |
| 50 | manualReview: { |
| 51 | open30to59d: open.days30to59d, |
| 52 | open60dPlus: open.days60plus, |
| 53 | otherStatus30dPlus: other.days30to59d + other.days60plus, |
| 54 | }, |
| 55 | }; |
| 56 | } |
| 57 | |
| 58 | export function assessMigrationCapacity(rows, now = new Date()) { |
| 59 | const summary = accumulateMigrationCapacityAssessment(createMigrationCapacityAssessment(), rows, now); |
| 60 | return finalizeMigrationCapacityAssessment(summary); |
| 61 | } |
| 62 |