Skip to content

Commit 34fb003

Browse files
committed
feat: calculate user final score
1 parent efe0399 commit 34fb003

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

lib/score.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import type {
2+
ContributionTotals,
3+
PullRequestNode,
4+
RepoNode,
5+
} from "@/types/github";
6+
import { PullRequestScoreDetail, RepoScoreDetail } from "@/types/score";
7+
8+
const LOG = Math.log;
9+
10+
11+
12+
function calculateRepoScore(
13+
repos: RepoNode[]
14+
): { total: number; details: RepoScoreDetail[] } {
15+
const details = repos.map((repo) => ({
16+
repo,
17+
score:
18+
LOG(repo.stargazerCount + 1) * 5 +
19+
LOG(repo.forkCount + 1) * 3 +
20+
LOG(repo.watchers.totalCount + 1) * 2,
21+
}));
22+
23+
details.sort((a, b) => b.score - a.score);
24+
25+
const total = details.reduce((sum, { score }, index) => {
26+
const weight = index < 5 ? 1 : 0.1;
27+
return sum + score * weight;
28+
}, 0);
29+
30+
return { total, details };
31+
}
32+
33+
function calculatePRScore(
34+
prs: PullRequestNode[],
35+
username: string
36+
): { total: number; details: PullRequestScoreDetail[] } {
37+
const grouped: Record<string, PullRequestScoreDetail[]> = {};
38+
39+
for (const pr of prs) {
40+
const repoOwner = pr.repository.owner.login.toLowerCase();
41+
if (repoOwner === username.toLowerCase()) continue; // ignore own repos
42+
if (!pr.merged) continue; // only merged PRs
43+
44+
const base = LOG(pr.repository.stargazerCount + 1) * 2;
45+
const sizeFactor = LOG(pr.additions + pr.deletions + 1);
46+
const score = base * sizeFactor;
47+
48+
const repoKey = pr.repository.nameWithOwner;
49+
grouped[repoKey] ||= [];
50+
grouped[repoKey].push({ pr, score });
51+
}
52+
53+
let total = 0;
54+
const allDetails: PullRequestScoreDetail[] = [];
55+
56+
for (const repoScores of Object.values(grouped)) {
57+
repoScores.sort((a, b) => b.score - a.score);
58+
const repoTotal = repoScores.reduce(
59+
(sum, item, i) => sum + item.score * (1 / (i + 1)),
60+
0
61+
);
62+
total += repoTotal;
63+
allDetails.push(...repoScores);
64+
}
65+
66+
allDetails.sort((a, b) => b.score - a.score);
67+
68+
return { total, details: allDetails };
69+
}
70+
71+
function calculateContributionScore(contrib: ContributionTotals): number {
72+
return (
73+
contrib.totalCommitContributions * 0.5 +
74+
contrib.totalPullRequestContributions * 2 +
75+
contrib.totalIssueContributions * 0.3
76+
);
77+
}
78+
79+
export function calculateUserScore(
80+
data: {
81+
repos: RepoNode[];
82+
pullRequests: PullRequestNode[];
83+
contributions: ContributionTotals;
84+
},
85+
username: string
86+
): {
87+
repoScore: number;
88+
prScore: number;
89+
contributionScore: number;
90+
finalScore: number;
91+
topRepos: { name: string; stars: number; forks: number; score: number }[];
92+
topPullRequests: { repo: string; stars: number; score: number }[];
93+
} {
94+
const repoScore = calculateRepoScore(data.repos);
95+
const prScore = calculatePRScore(data.pullRequests, username);
96+
const contributionScore = calculateContributionScore(data.contributions);
97+
98+
const finalScore =
99+
repoScore.total * 0.4 + prScore.total * 0.4 + contributionScore * 0.2;
100+
101+
return {
102+
repoScore: repoScore.total,
103+
prScore: prScore.total,
104+
contributionScore,
105+
finalScore,
106+
topRepos: repoScore.details.slice(0, 3).map((item) => ({
107+
name: item.repo.name,
108+
stars: item.repo.stargazerCount,
109+
forks: item.repo.forkCount,
110+
score: item.score,
111+
watchers: item.repo.watchers.totalCount,
112+
})),
113+
topPullRequests: prScore.details.slice(0, 3).map((item) => ({
114+
repo: item.pr.repository.nameWithOwner,
115+
title: item.pr.repository.nameWithOwner,
116+
stars: item.pr.repository.stargazerCount,
117+
score: item.score,
118+
})),
119+
};
120+
}

types/score.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { PullRequestNode, RepoNode } from "./github";
2+
3+
export type RepoScoreDetail = {
4+
repo: RepoNode;
5+
score: number;
6+
};
7+
8+
export type PullRequestScoreDetail = {
9+
pr: PullRequestNode;
10+
score: number;
11+
};

0 commit comments

Comments
 (0)