chore: update src/lib/analytics.ts

swadhinbiswas committed 8 months ago
d1173c4
1 parent 421bc6b
commit d1173c4
1 file changed
+31 -25
Showing 1 changed file+31-25
▼src/lib/analytics.ts
+31-25
2
2
import { getDatabase } from "../db";
3
3
import { type NodePgDatabase } from "drizzle-orm/node-postgres";
4
4
import * as schema from "../db/schema";
5
-import { eq, and, gte, desc, sql } from "drizzle-orm";
5
+import { eq, and, gte, desc, inArray } from "drizzle-orm";
6
6
import { pullRequests, pullRequestReviews } from "../db/schema/pull-requests";
7
7
8
8
const db = getDatabase() as NodePgDatabase<typeof schema>;
36
36
)
37
37
.orderBy(desc(pullRequests.mergedAt));
38
38
39
- // Also fetch reviews for review velocity
40
- const reviews = await db
41
- .select({
42
- prId: pullRequestReviews.pullRequestId,
43
- submittedAt: pullRequestReviews.submittedAt,
44
- })
45
- .from(pullRequestReviews)
46
- .innerJoin(pullRequests, eq(pullRequestReviews.pullRequestId, pullRequests.id))
47
- .where(
48
- and(
49
- eq(pullRequests.repositoryId, repoId),
50
- gte(pullRequestReviews.submittedAt, startDate)
51
- )
52
- );
39
+ const prIds = prs.map((pr) => pr.id);
40
+ const reviews = prIds.length
41
+ ? await db
42
+ .select({
43
+ prId: pullRequestReviews.pullRequestId,
44
+ submittedAt: pullRequestReviews.submittedAt,
45
+ createdAt: pullRequestReviews.createdAt,
46
+ })
47
+ .from(pullRequestReviews)
48
+ .where(inArray(pullRequestReviews.pullRequestId, prIds))
49
+ : [];
53
50
54
51
// Group by day
55
52
const statsMap = new Map<string, { totalCycle: number; totalMerge: number; totalReview: number; reviewCounts: number }>();
62
59
statsMap.set(dateStr, { totalCycle: 0, totalMerge: 0, totalReview: 0, reviewCounts: 0 });
63
60
}
64
61
62
+ const firstReviewByPr = new Map<string, Date>();
63
+
64
+ for (const review of reviews) {
65
+ const reviewTime = review.submittedAt || review.createdAt;
66
+ if (!reviewTime) continue;
67
+ const current = firstReviewByPr.get(review.prId);
68
+ if (!current || reviewTime < current) {
69
+ firstReviewByPr.set(review.prId, reviewTime);
70
+ }
71
+ }
72
+
65
73
// Calculate Metrics
66
74
prs.forEach((pr) => {
67
75
if (!pr.mergedAt || !pr.createdAt) return;
72
80
if (dayStats) {
73
81
dayStats.totalCycle += hours;
74
82
dayStats.totalMerge += 1;
83
+
84
+ const firstReviewAt = firstReviewByPr.get(pr.id);
85
+ if (firstReviewAt && firstReviewAt >= pr.createdAt) {
86
+ const reviewHours = (firstReviewAt.getTime() - pr.createdAt.getTime()) / (1000 * 60 * 60);
87
+ dayStats.totalReview += reviewHours;
88
+ dayStats.reviewCounts += 1;
89
+ }
75
90
}
76
91
});
77
92
78
- // Calculate Review Velocity (rough approx: merged PRs or any PR with review?)
79
- // Let's use the reviews we fetched.
80
- // We need to match review to its PR creation time.
81
- // This is a bit complex in one go. For MVP, let's simplify review velocity:
82
- // Average time for *merged* PRs to get their first review?
83
- // Let's iterate reviews and match to PRs if we have them.
84
- // Ideally we need `pullRequests` creation time for the reviews.
85
- // Let's stick to Cycle Time and Merge Frequency for MVP robustness.
86
-
87
93
return Array.from(statsMap.entries())
88
94
.map(([date, data]) => ({
89
95
date,
90
96
cycleTime: data.totalMerge ? Math.round((data.totalCycle / data.totalMerge) * 100) / 100 : 0,
91
97
mergeCount: data.totalMerge,
92
- reviewTime: 0 // Placeholder
98
+ reviewTime: data.reviewCounts ? Math.round((data.totalReview / data.reviewCounts) * 100) / 100 : 0
93
99
}))
94
100
.sort((a, b) => a.date.localeCompare(b.date));
95
101
}
96
102