/ opencodehub / src / lib / reviewers.ts
swadhinbiswas feat: OpenCodeHub v1.1.0 - Enterprise Features & Storage Adapters · 2/15/2026
719a1cb
46 lines · 1.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

import { getDatabase } from "../db";
import { type NodePgDatabase } from "drizzle-orm/node-postgres";
import * as schema from "../db/schema";
import { eq, and, count } from "drizzle-orm";
import { pullRequestReviewers, pullRequests } from "../db/schema/pull-requests";
import { users } from "../db/schema/users";

const db = getDatabase() as NodePgDatabase<typeof schema>;

/**
 * Suggests reviewers for a PR based on load balancing.
 * Strategy: Find users with the fewest OPEN review requests.
 */
export async function getSuggestedReviewers(
    repoId: string,
    excludeUserIds: string[] = []
) {
    const allUsers = await db.select().from(users);

    // Count open review requests per user
    const loadCounts = await db
        .select({
            userId: pullRequestReviewers.userId,
            pendingCount: count(pullRequestReviewers.id),
        })
        .from(pullRequestReviewers)
        .innerJoin(pullRequests, eq(pullRequestReviewers.pullRequestId, pullRequests.id))
        .where(eq(pullRequests.state, "open"))
        .groupBy(pullRequestReviewers.userId);

    const loadMap = new Map<string, number>();
    loadCounts.forEach((c) => loadMap.set(c.userId, Number(c.pendingCount)));

    const candidates = allUsers
        .filter((u) => !excludeUserIds.includes(u.id))
        .map((u) => ({
            user: u,
            load: loadMap.get(u.id) || 0,
        }))
        .sort((a, b) => a.load - b.load);

    // Return top 5 suggestions
    return candidates.slice(0, 5);
}