-
Notifications
You must be signed in to change notification settings - Fork 189
Leaderboard migration #1907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
SamXop123
wants to merge
4
commits into
recodehive:main
Choose a base branch
from
SamXop123:leaderboard-migration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+33
−426
Open
Leaderboard migration #1907
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -164,6 +164,7 @@ export function CommunityStatsProvider({ | |
| siteConfig: { customFields }, | ||
| } = useDocusaurusContext(); | ||
| const token = customFields?.gitToken || ""; | ||
| const backendApiUrl = (customFields?.backendApiUrl as string) || "http://localhost:5000"; | ||
|
|
||
| const [loading, setLoading] = useState(false); // Start with false to avoid hourglass | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
@@ -251,164 +252,6 @@ export function CommunityStatsProvider({ | |
| setCurrentTimeFilter(filter); | ||
| }, []); | ||
|
|
||
| const fetchAllOrgRepos = useCallback( | ||
| async (headers: Record<string, string>) => { | ||
| const repos: any[] = []; | ||
| let page = 1; | ||
| while (true) { | ||
| const resp = await fetch( | ||
| `https://api.github.com/orgs/${GITHUB_ORG}/repos?type=public&per_page=100&page=${page}`, | ||
| { | ||
| headers, | ||
| }, | ||
| ); | ||
| if (!resp.ok) { | ||
| throw new Error( | ||
| `Failed to fetch org repos: ${resp.status} ${resp.statusText}`, | ||
| ); | ||
| } | ||
| const data = await resp.json(); | ||
| repos.push(...data); | ||
| if (!Array.isArray(data) || data.length < 100) break; | ||
| page++; | ||
| } | ||
| return repos; | ||
| }, | ||
| [], | ||
| ); | ||
|
|
||
| const fetchMergedPRsForRepo = useCallback( | ||
| async (repoName: string, headers: Record<string, string>) => { | ||
| const mergedPRs: PullRequestItem[] = []; | ||
|
|
||
| // First, get the first page to estimate total pages | ||
| const firstResp = await fetch( | ||
| `https://api.github.com/repos/${GITHUB_ORG}/${repoName}/pulls?state=closed&per_page=100&page=1`, | ||
| { headers }, | ||
| ); | ||
|
|
||
| if (!firstResp.ok) { | ||
| console.warn( | ||
| `Failed to fetch PRs for ${repoName}: ${firstResp.status} ${firstResp.statusText}`, | ||
| ); | ||
| return []; | ||
| } | ||
|
|
||
| const firstPRs: PullRequestItem[] = await firstResp.json(); | ||
| if (!Array.isArray(firstPRs) || firstPRs.length === 0) return []; | ||
|
|
||
| const firstPageMerged = firstPRs.filter((pr) => Boolean(pr.merged_at)); | ||
| mergedPRs.push(...firstPageMerged); | ||
|
|
||
| // If we got less than 100, that's all there is | ||
| if (firstPRs.length < 100) return mergedPRs; | ||
|
|
||
| // Create parallel requests for remaining pages | ||
| const pagePromises: Promise<PullRequestItem[]>[] = []; | ||
| const maxPages = Math.min(MAX_PAGES_PER_REPO, 10); | ||
|
|
||
| for (let i = 2; i <= maxPages; i++) { | ||
| pagePromises.push( | ||
| fetch( | ||
| `https://api.github.com/repos/${GITHUB_ORG}/${repoName}/pulls?state=closed&per_page=100&page=${i}`, | ||
| { headers }, | ||
| ) | ||
| .then(async (resp) => { | ||
| if (!resp.ok) return []; | ||
| const prs: PullRequestItem[] = await resp.json(); | ||
| if (!Array.isArray(prs)) return []; | ||
| return prs.filter((pr) => Boolean(pr.merged_at)); | ||
| }) | ||
| .catch(() => []), | ||
| ); | ||
| } | ||
|
|
||
| // Wait for all pages in parallel | ||
| const remainingPages = await Promise.all(pagePromises); | ||
| remainingPages.forEach((pagePRs) => { | ||
| if (pagePRs.length > 0) mergedPRs.push(...pagePRs); | ||
| }); | ||
|
|
||
| return mergedPRs; | ||
| }, | ||
| [], | ||
| ); | ||
|
|
||
| // Enhanced processing function that stores only valid PRs with points | ||
| const processBatch = useCallback( | ||
| async ( | ||
| repos: any[], | ||
| headers: Record<string, string>, | ||
| ): Promise<{ | ||
| contributorMap: Map<string, FullContributor>; | ||
| totalMergedPRs: number; | ||
| }> => { | ||
| const contributorMap = new Map<string, FullContributor>(); | ||
| let totalMergedPRs = 0; | ||
|
|
||
| // Process repos in batches to control concurrency | ||
| for (let i = 0; i < repos.length; i += MAX_CONCURRENT_REQUESTS) { | ||
| const batch = repos.slice(i, i + MAX_CONCURRENT_REQUESTS); | ||
|
|
||
| const promises = batch.map(async (repo) => { | ||
| if (repo.archived) return { mergedPRs: [], repoName: repo.name }; | ||
|
|
||
| try { | ||
| const mergedPRs = await fetchMergedPRsForRepo(repo.name, headers); | ||
| return { mergedPRs, repoName: repo.name }; | ||
| } catch (error) { | ||
| console.warn(`Skipping repo ${repo.name} due to error:`, error); | ||
| return { mergedPRs: [], repoName: repo.name }; | ||
| } | ||
| }); | ||
|
|
||
| // Wait for current batch to complete | ||
| const results = await Promise.all(promises); | ||
|
|
||
| // Process results from this batch | ||
| results.forEach(({ mergedPRs, repoName }) => { | ||
| mergedPRs.forEach((pr) => { | ||
| // Calculate points for this PR based on labels | ||
| const prPoints = calculatePointsForPR(pr.labels); | ||
|
|
||
| // ONLY store PRs that have points (i.e., have "recode" label and a level label) | ||
| if (prPoints > 0) { | ||
| totalMergedPRs++; | ||
|
|
||
| const username = pr.user.login; | ||
| if (!contributorMap.has(username)) { | ||
| contributorMap.set(username, { | ||
| username, | ||
| avatar: pr.user.avatar_url, | ||
| profile: pr.user.html_url, | ||
| points: 0, // Will be calculated later based on filter | ||
| prs: 0, // Will be calculated later based on filter | ||
| allPRDetails: [], // Store only valid PRs here | ||
| }); | ||
| } | ||
| const contributor = contributorMap.get(username)!; | ||
|
|
||
| // Add detailed PR information only if it has all required fields | ||
| if (pr.title && pr.html_url && pr.merged_at && pr.number) { | ||
| contributor.allPRDetails.push({ | ||
| title: pr.title, | ||
| url: pr.html_url, | ||
| mergedAt: pr.merged_at, | ||
| repoName, | ||
| number: pr.number, | ||
| points: prPoints, | ||
| }); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| return { contributorMap, totalMergedPRs }; | ||
| }, | ||
| [fetchMergedPRsForRepo], | ||
| ); | ||
|
|
||
| const fetchAllStats = useCallback( | ||
| async (signal: AbortSignal) => { | ||
| // Check cache first and load it immediately without showing loading state | ||
|
|
@@ -433,57 +276,52 @@ export function CommunityStatsProvider({ | |
|
|
||
| setError(null); | ||
|
|
||
| if (!token) { | ||
| setError( | ||
| "GitHub token not found. Please set customFields.gitToken in docusaurus.config.js.", | ||
| ); | ||
| setLoading(false); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| const headers: Record<string, string> = { | ||
| Authorization: `token ${token}`, | ||
| Accept: "application/vnd.github.v3+json", | ||
| }; | ||
|
|
||
| // Fetch both org stats and repos in parallel | ||
| const [orgStats, repos] = await Promise.all([ | ||
| githubService.fetchOrganizationStats(signal), | ||
| fetchAllOrgRepos(headers), | ||
| const [leaderboardResp, statsResp] = await Promise.all([ | ||
| fetch(`${backendApiUrl}/api/leaderboard`, { signal }), | ||
| fetch(`${backendApiUrl}/api/stats`, { signal }) | ||
| ]); | ||
|
|
||
| // Set org stats immediately | ||
| setGithubStarCount(orgStats.totalStars); | ||
| setGithubContributorsCount(orgStats.totalContributors); | ||
| setGithubForksCount(orgStats.totalForks); | ||
| setGithubReposCount(orgStats.publicRepositories); | ||
| setGithubDiscussionsCount(orgStats.discussionsCount); | ||
| setLastUpdated(new Date(orgStats.lastUpdated)); | ||
|
|
||
| // Process leaderboard data with concurrent processing | ||
| const { contributorMap, totalMergedPRs } = await processBatch( | ||
| repos, | ||
| headers, | ||
| ); | ||
| if (!leaderboardResp.ok || !statsResp.ok) { | ||
| throw new Error("Failed to fetch leaderboard data from backend server"); | ||
| } | ||
|
|
||
| const contributorsArray = Array.from(contributorMap.values()); | ||
| const leaderboardData = await leaderboardResp.json(); | ||
| const statsData = await statsResp.json(); | ||
|
|
||
| // Set org stats immediately | ||
| setGithubStarCount(statsData.totalStars); | ||
| setGithubContributorsCount(statsData.totalContributors); | ||
| setGithubForksCount(statsData.totalForks); | ||
| setGithubReposCount(statsData.publicRepositories); | ||
| setGithubDiscussionsCount(statsData.discussionsCount); | ||
| setLastUpdated(new Date(statsData.lastUpdated)); | ||
|
|
||
| // Format to FullContributor (which matches contributor mapping) | ||
| const contributorsArray: FullContributor[] = (leaderboardData.contributors || []).map((c: any) => ({ | ||
| username: c.username, | ||
| avatar: c.avatar, | ||
| profile: c.profile, | ||
| points: c.points, | ||
| prs: c.prs, | ||
| allPRDetails: c.prDetails || [] // Backend stores complete list | ||
| })); | ||
|
|
||
| setAllContributors(contributorsArray); | ||
|
|
||
| // Cache the results (raw data without filtering) | ||
| setCache({ | ||
| data: { | ||
| contributors: contributorsArray, | ||
| rawStats: { totalPRs: totalMergedPRs }, | ||
| rawStats: { totalPRs: statsData.totalContributors }, | ||
| }, | ||
|
Comment on lines
314
to
317
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Adez017 I need your review on merged PR This is the backend code. |
||
| timestamp: now, | ||
| }); | ||
| } catch (err: any) { | ||
| if (err.name !== "AbortError") { | ||
| console.error("Error fetching GitHub organization stats:", err); | ||
| console.error("Error fetching stats from backend:", err); | ||
| setError( | ||
| err instanceof Error ? err.message : "Failed to fetch GitHub stats", | ||
| err instanceof Error ? err.message : "Failed to fetch stats from backend", | ||
| ); | ||
|
|
||
| // Set fallback values on error | ||
|
|
@@ -497,7 +335,7 @@ export function CommunityStatsProvider({ | |
| setLoading(false); | ||
| } | ||
| }, | ||
| [token, fetchAllOrgRepos, processBatch, cache], | ||
| [backendApiUrl, cache], | ||
| ); | ||
|
|
||
| const clearCache = useCallback(() => { | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think now we don't need to mount the git token , let me know if it is required or we can move forward and remove this one @SamXop123