Created
August 27, 2025 15:18
-
-
Save zgorizzo69/5e960865819e4a6212e741e566d83a51 to your computer and use it in GitHub Desktop.
gives the number of PR reviewed and line addition + deletion by user on the usual-dao organisation
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
| #!/usr/bin/env node | |
| /** | |
| * Script to get the number of PRs reviewed by you in the usual-dao organization in 2025 | |
| * | |
| * Usage: | |
| * 1. Set your GitHub username in the GITHUB_USERNAME variable below | |
| * 2. Set your GitHub personal access token in the GITHUB_TOKEN environment variable | |
| * 3. Run: node get-pr-reviews.js | |
| * | |
| * To create a GitHub token: | |
| * 1. Go to https://github.com/settings/tokens | |
| * 2. Generate a new token with 'repo' and 'read:org' scopes | |
| * 3. Export it: export GITHUB_TOKEN="your_token_here" | |
| */ | |
| const https = require('https'); | |
| // Configuration | |
| const GITHUB_USERNAME = 'YOUR_GITHUB_USERNAME'; // Replace with your GitHub username | |
| const GITHUB_TOKEN = process.env.GITHUB_TOKEN; | |
| const ORG = 'usual-dao'; | |
| const YEAR = '2025'; | |
| if (!GITHUB_TOKEN) { | |
| console.error('Error: Please set your GITHUB_TOKEN environment variable'); | |
| console.error('Example: export GITHUB_TOKEN="your_token_here"'); | |
| process.exit(1); | |
| } | |
| if (GITHUB_USERNAME === 'YOUR_GITHUB_USERNAME') { | |
| console.error('Error: Please set your GitHub username in the GITHUB_USERNAME variable'); | |
| process.exit(1); | |
| } | |
| function makeGitHubRequest(path) { | |
| return new Promise((resolve, reject) => { | |
| const options = { | |
| hostname: 'api.github.com', | |
| path: path, | |
| method: 'GET', | |
| headers: { | |
| 'Authorization': `token ${GITHUB_TOKEN}`, | |
| 'User-Agent': 'PR-Review-Counter', | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }; | |
| const req = https.request(options, (res) => { | |
| let data = ''; | |
| res.on('data', (chunk) => { | |
| data += chunk; | |
| }); | |
| res.on('end', () => { | |
| if (res.statusCode === 200) { | |
| resolve(JSON.parse(data)); | |
| } else { | |
| reject(new Error(`GitHub API error: ${res.statusCode} ${res.statusMessage}\n${data}`)); | |
| } | |
| }); | |
| }); | |
| req.on('error', (error) => { | |
| reject(error); | |
| }); | |
| req.end(); | |
| }); | |
| } | |
| async function getAllRepos() { | |
| console.log(`Fetching repositories for organization: ${ORG}...`); | |
| const repos = []; | |
| let page = 1; | |
| while (true) { | |
| try { | |
| const data = await makeGitHubRequest(`/orgs/${ORG}/repos?page=${page}&per_page=100`); | |
| if (data.length === 0) break; | |
| repos.push(...data.map(repo => repo.name)); | |
| page++; | |
| } catch (error) { | |
| console.error('Error fetching repositories:', error.message); | |
| break; | |
| } | |
| } | |
| console.log(`Found ${repos.length} repositories in ${ORG}`); | |
| return repos; | |
| } | |
| async function getPRsWithReviews(repo) { | |
| console.log(`Checking repository: ${repo}...`); | |
| const reviewedPRs = []; | |
| let page = 1; | |
| while (true) { | |
| try { | |
| // Get all PRs updated in 2025 | |
| const prs = await makeGitHubRequest( | |
| `/repos/${ORG}/${repo}/pulls?state=all&page=${page}&per_page=100&sort=updated&direction=desc` | |
| ); | |
| if (prs.length === 0) break; | |
| // Filter PRs updated in 2025 | |
| const prs2025 = prs.filter(pr => { | |
| const updatedAt = new Date(pr.updated_at); | |
| return updatedAt.getFullYear() === 2025; | |
| }); | |
| if (prs2025.length === 0) { | |
| // If no PRs from 2025 on this page, we can stop (since they're sorted by updated date) | |
| break; | |
| } | |
| // Check reviews for each PR | |
| for (const pr of prs2025) { | |
| try { | |
| const reviews = await makeGitHubRequest(`/repos/${ORG}/${repo}/pulls/${pr.number}/reviews`); | |
| // Check if you reviewed this PR in 2025 | |
| const yourReviews = reviews.filter(review => { | |
| const reviewDate = new Date(review.submitted_at); | |
| return review.user.login === GITHUB_USERNAME && | |
| reviewDate.getFullYear() === 2025; | |
| }); | |
| if (yourReviews.length > 0) { | |
| // Get PR details including line counts | |
| const prDetails = await makeGitHubRequest(`/repos/${ORG}/${repo}/pulls/${pr.number}`); | |
| reviewedPRs.push({ | |
| id: `${repo}#${pr.number}`, | |
| title: pr.title, | |
| additions: prDetails.additions || 0, | |
| deletions: prDetails.deletions || 0, | |
| changedFiles: prDetails.changed_files || 0, | |
| reviewCount: yourReviews.length | |
| }); | |
| } | |
| } catch (error) { | |
| console.error(`Error fetching reviews for ${repo}#${pr.number}:`, error.message); | |
| } | |
| } | |
| page++; | |
| } catch (error) { | |
| console.error(`Error fetching PRs for ${repo}:`, error.message); | |
| break; | |
| } | |
| } | |
| return reviewedPRs; | |
| } | |
| async function main() { | |
| console.log(`Counting PRs reviewed by ${GITHUB_USERNAME} in ${ORG} organization for ${YEAR}...\n`); | |
| try { | |
| const repos = await getAllRepos(); | |
| const allReviewedPRs = []; | |
| for (const repo of repos) { | |
| const reviewedPRs = await getPRsWithReviews(repo); | |
| allReviewedPRs.push(...reviewedPRs); | |
| } | |
| // Calculate totals | |
| const totalPRs = allReviewedPRs.length; | |
| const totalAdditions = allReviewedPRs.reduce((sum, pr) => sum + pr.additions, 0); | |
| const totalDeletions = allReviewedPRs.reduce((sum, pr) => sum + pr.deletions, 0); | |
| const totalChangedFiles = allReviewedPRs.reduce((sum, pr) => sum + pr.changedFiles, 0); | |
| const totalLinesReviewed = totalAdditions + totalDeletions; | |
| console.log('\n' + '='.repeat(60)); | |
| console.log(`RESULTS FOR ${YEAR}`); | |
| console.log('='.repeat(60)); | |
| console.log(`Organization: ${ORG}`); | |
| console.log(`Reviewer: ${GITHUB_USERNAME}`); | |
| console.log(`Total PRs reviewed: ${totalPRs}`); | |
| console.log(`Total lines reviewed: ${totalLinesReviewed.toLocaleString()}`); | |
| console.log(` - Lines added: ${totalAdditions.toLocaleString()}`); | |
| console.log(` - Lines deleted: ${totalDeletions.toLocaleString()}`); | |
| console.log(`Total files changed: ${totalChangedFiles.toLocaleString()}`); | |
| if (totalPRs > 0) { | |
| console.log(`\nAverage per PR:`); | |
| console.log(` - Lines per PR: ${Math.round(totalLinesReviewed / totalPRs).toLocaleString()}`); | |
| console.log(` - Files per PR: ${Math.round(totalChangedFiles / totalPRs)}`); | |
| } | |
| if (allReviewedPRs.length > 0) { | |
| console.log('\n' + '='.repeat(60)); | |
| console.log('DETAILED BREAKDOWN:'); | |
| console.log('='.repeat(60)); | |
| // Sort by total lines (additions + deletions) in descending order | |
| allReviewedPRs.sort((a, b) => (b.additions + b.deletions) - (a.additions + a.deletions)); | |
| allReviewedPRs.forEach(pr => { | |
| const totalLines = pr.additions + pr.deletions; | |
| console.log(`${pr.id}: ${totalLines.toLocaleString()} lines (+${pr.additions.toLocaleString()}/-${pr.deletions.toLocaleString()}) in ${pr.changedFiles} files`); | |
| console.log(` Title: ${pr.title}`); | |
| console.log(''); | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error.message); | |
| process.exit(1); | |
| } | |
| } | |
| main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment