Skip to content

Commit

Permalink
v3.1.0: More stats available
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelmhtr committed Dec 22, 2024
1 parent 593c720 commit 27b5bb7
Show file tree
Hide file tree
Showing 25 changed files with 433 additions and 86 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.

## [3.1.0] - 2024-12-22
### Added
- Adds new available stats: `reviewedAdditions`, `reviewedDeletions`, `reviewedLines`, `totalObservations`, `medianObservations`, `revisionSuccessRate`, `additions`, `deletions` and `lines`.

### Changes
- `commentsPerReview` is calculated now as the median, not the average.

## [3.0.0] - 2024-12-12
### Added
- New `stats` option to specify the stats to be calculated.
Expand Down
45 changes: 34 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
[![CI](https://github.com/flowwer-dev/pull-request-stats/workflows/Tests/badge.svg)](https://github.com/flowwer-dev/pull-request-stats/actions?query=workflow%3ATests)
[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Pull%20Request%20Stats-blue.svg?colorA=24292e&colorB=0366d6&style=flat&longCache=true&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6wAADOsB5dZE0gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAERSURBVCiRhZG/SsMxFEZPfsVJ61jbxaF0cRQRcRJ9hlYn30IHN/+9iquDCOIsblIrOjqKgy5aKoJQj4O3EEtbPwhJbr6Te28CmdSKeqzeqr0YbfVIrTBKakvtOl5dtTkK+v4HfA9PEyBFCY9AGVgCBLaBp1jPAyfAJ/AAdIEG0dNAiyP7+K1qIfMdonZic6+WJoBJvQlvuwDqcXadUuqPA1NKAlexbRTAIMvMOCjTbMwl1LtI/6KWJ5Q6rT6Ht1MA58AX8Apcqqt5r2qhrgAXQC3CZ6i1+KMd9TRu3MvA3aH/fFPnBodb6oe6HM8+lYHrGdRXW8M9bMZtPXUji69lmf5Cmamq7quNLFZXD9Rq7v0Bpc1o/tp0fisAAAAASUVORK5CYII=)](https://github.com/marketplace/actions/pull-request-stats)

Github action to print relevant stats about Pull Request reviewers.
Github action to print relevant [stats](#stats) about Pull Request.

The objective of this action is to:

* Track and increase your team's performance.
* Reduce the time taken to review the pull requests.
* Encourage quality on reviews.
* Help to decide which people to assign as reviewers.
Expand Down Expand Up @@ -52,7 +53,7 @@ The possible inputs for this action are:
| `organization` | If you prefer, you may specify your organization's name to calculate the stats across all of its repositories. When specifying an organization, **it is mandatory to pass a Personal Access Token** in the `token` parameter. | `null` |
| `period` | The period used to calculate the stats, expressed in days. | `30` |
| `limit` | The maximum number of rows to display in the table. A value of `0` means unlimited. | `0` |
| `stats` | A comma-separated list of stats to calculate and display. Possible values: `totalReviews`, `timeToReview`, `totalComments`, `commentsPerReview`, `openedPullRequests`. For details on each stats check the [Stats](#stats) section. | `totalReviews,timeToReview,totalComments` |
| `stats` | A comma-separated list of stats to calculate and display. Possible values: `totalReviews`, `timeToReview`, `totalComments`, etc... (Check all available in the [Stats](#stats) section) | `totalReviews,timeToReview,totalComments` |
| `charts` | Whether to add a chart to the start. Possible values: `true` or `false`. | `false` |
| `disableLinks` | If `true`, removes the links to the detailed charts. Possible values: `true` or `false`. | `false` |
| `sortBy` | The column used to sort the data. Possible values: `totalReviews`, `timeToReview`, `totalComments`, `commentsPerReview`, `openedPullRequests`. | `totalReviews` |
Expand Down Expand Up @@ -137,7 +138,7 @@ jobs:
charts: true
disableLinks: true
sortBy: 'totalComments'
stats: 'totalComments,openedPullRequests'
stats: 'totalComments,openedPullRequests,totalReviews'
```

This config will:
Expand All @@ -146,11 +147,11 @@ This config will:
* Display charts for the metrics.
* Remove the links to detailed charts.
* Sort results by the "Total comments" column.
* Show the "Total comments" and "Opened pull requests" columns (in that order).
* Show the "Total comments", "Opened pull requests"and "Median time to review" columns (in that order).

and print a table like this:

| | User | Total comments | Total reviews | Median time to review |
| | User | Total comments | Opened pull requests | Median time to review |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------------------------- | ------------------ | ------------------- |
| <a href="https://github.com/manuelmhtr"><img src="https://avatars2.githubusercontent.com/u/1031639" width="32"></a> | manuelmhtr<br/>🥇 | **12**<br/>▀▀▀▀▀▀▀▀ | **8**<br/>▀▀▀▀ | 53m<br/> |
| <a href="https://github.com/CarlosCRG19"><img src="https://avatars.githubusercontent.com/u/61464973" width="32"></a> | CarlosCRG19<br/>🥈 | 3<br/>▀▀ | 4<br/>▀▀ | 58m<br/> |
Expand All @@ -162,13 +163,35 @@ and print a table like this:

## Stats

The stats are calculated as follows:
This section explains the available stats that can be displayed using the [`stats` option](#action-inputs) and how they are calculated.

* **Total reviews (`totalReviews`):** The count of all Pull Requests reviewed by a person in the period.
* **Time to review (`timeToReview`):** The **median** time a reviewer takes from the _Pull Request publication_ or the last _Commit push_ (whatever happens last) to the first time the pull request is reviewed.
* **Total comments (`totalComments`):** The number of comments made while reviewing other users' Pull Requests during the specified period. Comments made on your own PRs or general PR discussions are excluded; only comments directly related to code are counted.
* **Comments per review (`commentsPerReview`):** The **average** comments the reviewer made on the pull requests.
* **Opened pull requests (`openedPullRequests`):** The number of pull requests opened by the user in the period.
### Review stats

Stats related to the review process:

| Stat name and ID | Description |
| ---------------- | ----------- |
| **Total reviews (`totalReviews`):** | The total number of pull requests reviewed by a user during the specified period. |
| **Total comments (`totalComments`):** | The number of comments made while reviewing other users' Pull Requests during the specified period. Excludes comments made on your own pull requests or general discussions; only code-related comments are included. |
| **Time to review (`timeToReview`):** | The **median** time a reviewer takes from the _Pull Request publication_ or the last _commit push_ (whatever happens last) to the first time the pull request is reviewed. |
| **Comments per review (`commentsPerReview`):** | The **median** number of comments a reviewer made per pull request. |
| **Reviewed additions (`reviewedAdditions`):** | The total number of added lines reviewed. |
| **Reviewed deletions (`reviewedDeletions`):** | The total number of deleted lines reviewed. |
| **Reviewed lines (`reviewedLines`):** | The total number of lines reviewed (additions + deletions). |

### Performance stats:

Stats related to the production process (opened pull requests):

| Stat name and ID | Description |
| ---------------- | ----------- |
| **Opened pull requests (`openedPullRequests`):** | The number of pull requests opened by the user in the period. |
| **Total observations (`totalObservations`):** | The total number of comments received on pull requests opened by the user. |
| **Median observations (`medianObservations`):** | The **median** number of comments received per pull request opened by the user. |
| **Revision success rate (`revisionSuccessRate`):** | The percentage of pull request reviews resulting in approval, compared to the total reviews received. (eg. After 2 "Comment", 4 "Request changes" and 3 "Approve" reviews, the success rate would be `0.33`)|
| **Additions (`additions`):** | The total number of added lines across the opened pull requests. |
| **Deletions (`deletions`):** | The total number of deleted lines across the opened pull requests. |
| **Lines (`lines`):** | The total number of lines changed (added and deleted) across all pull requests opened by the user. |

## Integrations 🔌

Expand Down
149 changes: 124 additions & 25 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42002,28 +42002,73 @@ const STATS = {
totalReviews: {
id: 'totalReviews',
sortOrder: 'DESC',
parser: noParse,
parser: toFixed(0),
},
totalComments: {
id: 'totalComments',
sortOrder: 'DESC',
parser: toFixed(0),
},
timeToReview: {
id: 'timeToReview',
sortOrder: 'ASC',
parser: durationToString,
},
totalComments: {
id: 'totalComments',
sortOrder: 'DESC',
parser: noParse,
},
commentsPerReview: {
id: 'commentsPerReview',
sortOrder: 'DESC',
parser: toFixed(2),
},
reviewedAdditions: {
id: 'reviewedAdditions',
sortOrder: 'DESC',
parser: toFixed(0),
},
reviewedDeletions: {
id: 'reviewedDeletions',
sortOrder: 'DESC',
parser: toFixed(0),
},
reviewedLines: {
id: 'reviewedLines',
sortOrder: 'DESC',
parser: toFixed(0),
},
openedPullRequests: {
id: 'openedPullRequests',
sortOrder: 'DESC',
parser: noParse,
},
totalObservations: {
id: 'totalObservations',
sortOrder: 'DESC',
parser: toFixed(0),
},
medianObservations: {
id: 'medianObservations',
sortOrder: 'DESC',
parser: toFixed(2),
},
revisionSuccessRate: {
id: 'revisionSuccessRate',
sortOrder: 'DESC',
parser: toFixed(2),
},
additions: {
id: 'additions',
sortOrder: 'DESC',
parser: toFixed(0),
},
deletions: {
id: 'deletions',
sortOrder: 'DESC',
parser: toFixed(0),
},
lines: {
id: 'lines',
sortOrder: 'DESC',
parser: toFixed(0),
},
};

const VALID_STATS = Object.keys(STATS);
Expand Down Expand Up @@ -42252,11 +42297,15 @@ const PRS_QUERY = `
node {
... on PullRequest {
id
additions
deletions
publishedAt
author { ...ActorFragment }
reviews(first: 100) {
nodes {
id
body
state
submittedAt
commit { pushedDate }
comments { totalCount }
Expand Down Expand Up @@ -43043,13 +43092,40 @@ module.exports = async ({
/***/ }),

/***/ 8216:
/***/ ((module) => {
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {

const { sum, median, divide } = __nccwpck_require__(5494);

const getProperty = (list, prop) => list.map((el) => el[prop]);

const removeOwnPulls = ({ isOwnPull }) => !isOwnPull;

const removeWithEmptyId = ({ id }) => !!id;

module.exports = (pulls) => {
const openedPullRequests = pulls.length;
const reviews = pulls
.reduce((acc, pull) => ([...acc, ...pull.reviews]), [])
.filter(removeOwnPulls)
.filter(removeWithEmptyId);

const approvedReviews = reviews.filter(({ isApproved }) => isApproved);
const observationsList = getProperty(reviews, 'commentsCount');
const totalObservations = sum(observationsList);
const medianObservations = median(observationsList);
const totalApprovedReviews = approvedReviews.length || 0;
const additions = sum(getProperty(pulls, 'additions'));
const deletions = sum(getProperty(pulls, 'deletions'));
const lines = additions + deletions;

return {
openedPullRequests,
totalObservations,
medianObservations,
revisionSuccessRate: divide(totalApprovedReviews, reviews.length),
additions,
deletions,
lines,
};
};

Expand All @@ -43065,10 +43141,7 @@ module.exports = (pulls) => {

if (!acc[userId]) acc[userId] = { userId, pullRequests: [] };

acc[userId].pullRequests.push({
id: pull.id,
submittedAt: pull.submittedAt,
});
acc[userId].pullRequests.push(pull);
return acc;
}, {});

Expand Down Expand Up @@ -43142,20 +43215,28 @@ module.exports = ({
/***/ 4271:
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {

const { sum, median, divide } = __nccwpck_require__(5494);
const { sum, median } = __nccwpck_require__(5494);

const getProperty = (list, prop) => list.map((el) => el[prop]);

module.exports = (reviews) => {
const pullRequestIds = getProperty(reviews, 'pullRequestId');
const totalReviews = new Set(pullRequestIds).size;
const totalComments = sum(getProperty(reviews, 'commentsCount'));
module.exports = (reviews, pullsById) => {
const pullRequestIds = new Set(getProperty(reviews, 'pullRequestId'));
const totalReviews = pullRequestIds.size;
const commentsCountList = getProperty(reviews, 'commentsCount');
const totalComments = sum(commentsCountList);
const pullRequests = [...pullRequestIds].map((id) => pullsById[id]);
const reviewedAdditions = sum(getProperty(pullRequests, 'additions'));
const reviewedDeletions = sum(getProperty(pullRequests, 'deletions'));
const reviewedLines = reviewedAdditions + reviewedDeletions;

return {
totalReviews,
totalComments,
commentsPerReview: divide(totalComments, totalReviews),
timeToReview: median(getProperty(reviews, 'timeToReview')),
commentsPerReview: median(commentsCountList),
reviewedAdditions,
reviewedDeletions,
reviewedLines,
};
};

Expand Down Expand Up @@ -43200,11 +43281,15 @@ module.exports = (pulls) => {
const calculateReviewsStats = __nccwpck_require__(4271);
const groupReviews = __nccwpck_require__(7308);

module.exports = (pulls) => groupReviews(pulls)
.map(({ userId, reviews }) => {
const stats = calculateReviewsStats(reviews);
return { userId, reviews, stats };
});
module.exports = (pulls) => {
const pullsById = pulls.reduce((acc, pull) => ({ ...acc, [pull.id]: pull }), {});

return groupReviews(pulls)
.map(({ userId, reviews }) => {
const stats = calculateReviewsStats(reviews, pullsById);
return { userId, reviews, stats };
});
};


/***/ }),
Expand Down Expand Up @@ -44148,13 +44233,18 @@ const getFilteredReviews = (data) => get(data, 'node.reviews.nodes', []).filter(
module.exports = (data = {}) => {
const author = parseUser(get(data, 'node.author'));
const publishedAt = new Date(get(data, 'node.publishedAt'));
const additions = get(data, 'node.additions');
const deletions = get(data, 'node.deletions');
const handleReviews = (review) => parseReview(review, { publishedAt, authorLogin: author.login });

return {
author,
additions,
deletions,
publishedAt,
cursor: data.cursor,
id: get(data, 'node.id'),
lines: additions + deletions,
reviews: getFilteredReviews(data).map(handleReviews),
};
};
Expand All @@ -44168,19 +44258,28 @@ module.exports = (data = {}) => {
const get = __nccwpck_require__(615);
const parseUser = __nccwpck_require__(4203);

const APPROVED = 'APPROVED';

module.exports = (data = {}, pullRequest = {}) => {
const author = parseUser(data.author);
const isOwnPull = author.login === pullRequest.authorLogin;
const submittedAt = new Date(data.submittedAt);
const body = get(data, 'body');
const state = get(data, 'state');
const commitDate = new Date(get(data, 'commit.pushedDate'));
const startDate = Math.max(pullRequest.publishedAt, commitDate);
const hasBody = !!((body || '').trim());
const extraComment = hasBody ? 1 : 0;

return {
author,
isOwnPull,
submittedAt,
body,
id: get(data, 'id'),
commentsCount: get(data, 'comments.totalCount'),
state: get(data, 'state'),
isApproved: state === APPROVED,
commentsCount: get(data, 'comments.totalCount') + extraComment,
timeToReview: submittedAt - startDate,
};
};
Expand Down Expand Up @@ -49526,7 +49625,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"name":"mixpanel","description":"A si
/***/ ((module) => {

"use strict";
module.exports = /*#__PURE__*/JSON.parse('{"name":"pull-request-stats","version":"3.0.0","description":"Github action to print relevant stats about Pull Request reviewers","main":"dist/index.js","type":"commonjs","scripts":{"build":"eslint src && ncc build src/index.js -o dist -a","test":"jest","lint":"eslint ./"},"keywords":[],"author":"Manuel de la Torre","license":"MIT","jest":{"testEnvironment":"node","testMatch":["**/?(*.)+(spec|test).[jt]s?(x)"]},"dependencies":{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","axios":"^1.7.9","humanize-duration":"^3.32.1","i18n-js":"^3.9.2","jsurl":"^0.1.5","lodash.get":"^4.4.2","markdown-table":"^2.0.0","mixpanel":"^0.18.0"},"devDependencies":{"@eslint/eslintrc":"^3.2.0","@eslint/js":"^9.16.0","@vercel/ncc":"^0.38.3","eslint":"^9.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-import":"^2.31.0","eslint-plugin-jest":"^28.9.0","globals":"^15.13.0","jest":"^29.7.0"},"funding":"https://github.com/sponsors/manuelmhtr","packageManager":"[email protected]"}');
module.exports = /*#__PURE__*/JSON.parse('{"name":"pull-request-stats","version":"3.1.0","description":"Github action to print relevant stats about Pull Request reviewers","main":"dist/index.js","type":"commonjs","scripts":{"build":"eslint src && ncc build src/index.js -o dist -a","test":"jest","lint":"eslint ./"},"keywords":[],"author":"Manuel de la Torre","license":"MIT","jest":{"testEnvironment":"node","testMatch":["**/?(*.)+(spec|test).[jt]s?(x)"]},"dependencies":{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","axios":"^1.7.9","humanize-duration":"^3.32.1","i18n-js":"^3.9.2","jsurl":"^0.1.5","lodash.get":"^4.4.2","markdown-table":"^2.0.0","mixpanel":"^0.18.0"},"devDependencies":{"@eslint/eslintrc":"^3.2.0","@eslint/js":"^9.16.0","@vercel/ncc":"^0.38.3","eslint":"^9.16.0","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-import":"^2.31.0","eslint-plugin-jest":"^28.9.0","globals":"^15.13.0","jest":"^29.7.0"},"funding":"https://github.com/sponsors/manuelmhtr","packageManager":"[email protected]"}');

/***/ }),

Expand All @@ -49550,7 +49649,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"slack":{"logs":{"notConfigured":"Sla
/***/ ((module) => {

"use strict";
module.exports = /*#__PURE__*/JSON.parse('{"title":"Pull reviewers stats","icon":"https://s3.amazonaws.com/manuelmhtr.assets/flowwer/logo/logo-1024px.png","subtitle":{"one":"Stats of the last day for {{sources}}","other":"Stats of the last {{count}} days for {{sources}}"},"sources":{"separator":", ","fullList":"{{firsts}} and {{last}}","andOthers":"{{firsts}} and {{count}} others"},"columns":{"avatar":"","username":"User","commentsPerReview":"Comments per review","timeToReview":"Time to review","totalReviews":"Total reviews","totalComments":"Total comments","openedPullRequests":"Opened PRs"},"footer":"<sup>⚡️ [Pull request stats](https://bit.ly/pull-request-stats)</sup>"}');
module.exports = /*#__PURE__*/JSON.parse('{"title":"Pull reviewers stats","icon":"https://s3.amazonaws.com/manuelmhtr.assets/flowwer/logo/logo-1024px.png","subtitle":{"one":"Stats of the last day for {{sources}}","other":"Stats of the last {{count}} days for {{sources}}"},"sources":{"separator":", ","fullList":"{{firsts}} and {{last}}","andOthers":"{{firsts}} and {{count}} others"},"columns":{"avatar":"","username":"User","totalReviews":"Total reviews","totalComments":"Total comments","timeToReview":"Time to review","commentsPerReview":"Comments per review","reviewedAdditions":"Reviewed additions","reviewedDeletions":"Reviewed deletions","reviewedLines":"Reviewed lines","openedPullRequests":"Opened PRs","totalObservations":"Total observations","medianObservations":"Observations per PR","revisionSuccessRate":"Revision success rate","additions":"Additions","deletions":"Deletions","lines":"Lines of code"},"footer":"<sup>⚡️ [Pull request stats](https://bit.ly/pull-request-stats)</sup>"}');

/***/ })

Expand Down
Loading

0 comments on commit 27b5bb7

Please sign in to comment.