From 9f6cad9deaeb2fc4b6363197c32c6c0b248e0708 Mon Sep 17 00:00:00 2001 From: Aleksei Simatov Date: Fri, 13 Jun 2025 21:59:05 +0700 Subject: [PATCH 1/2] Feature: Exclude/Include users from report --- README.md | 6 +- action.yml | 6 + build/index.js | 352 ++++++++++-------- package.json | 2 +- src/converters/collectData.ts | 20 +- .../utils/calculations/checkUserInclusive.ts | 18 +- .../utils/calculations/getApproveTime.ts | 4 + .../utils/calculations/getResponses.ts | 10 + src/converters/utils/calculations/index.ts | 1 + src/converters/utils/prepareActionsTime.ts | 33 +- .../utils/prepareConductedReviews.ts | 2 + src/converters/utils/prepareDiscussions.ts | 260 ++++++------- .../utils/preparePullRequestTimeline.ts | 8 +- .../utils/prepareRequestedReviews.ts | 16 +- src/converters/utils/prepareResponseTime.ts | 100 ++--- src/converters/utils/prepareReviews.ts | 14 +- src/view/createMarkdown.ts | 4 +- src/view/utils/createConfigParamsCode.ts | 2 + 18 files changed, 456 insertions(+), 402 deletions(-) diff --git a/README.md b/README.md index 2719a77..094f4f7 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Shows the total volume of code merged, reviews conducted, and comments in PRs. H | **dev3** | 17 | 17 | 0 | 1 | 1 | +1305/-310 | 14/1/1/0/1 | | **total** | 78 | 77 | 0 | 3 | 4 | +8395/-3479 | 51/15/5/3/4 | -### Discussion Intensity (Author’s Perspective) +### Discussion Intensity (Author's Perspective) Measures how discussion-heavy PRs are from the author's perspective, based on open discussions, review statuses, and the number of comments. Additionally, you can track discussion topics and user agreement by adding discussion topics in `[[]]` and using thumbs up/down ( :+1: / :-1: ) reactions on the opening comment. Use the `pr-quality` value in the `SHOW_STATS_TYPES` parameter. @@ -118,7 +118,7 @@ title Discussions types total 12/2023 "Formatting(9)":9 ``` -### Discussion Intensity (Reviewer’s Perspective) +### Discussion Intensity (Reviewer's Perspective) Measures how discussion-heavy PRs are from the reviewer's perspective, based on discussions, comments, and PR statuses. Helps understand reviewer engagement and decision-making. Use the `code-review-engagement` value in the `SHOW_STATS_TYPES` parameter and add thumbs up/down ( :+1: / :-1: ) reactions on opening comments. @@ -333,6 +333,8 @@ Below is a table outlining the various configuration parameters available for ** | `SHOW_USERS` | Displays only specified users in reports, but includes all users in the background analytics. Use `total` to show total stats. Users should be separated by commas. | - | | `EXCLUDE_LABELS` | PRs with mentioned labels will be excluded from the report . Values should be separated by commas. Example: `bugfix, enhancement` | - | | `INCLUDE_LABELS` | Only PRs with mentioned labels will be included in the report. Values should be separated by commas. Example: `bugfix, enhancement` | - | +| `INCLUDE_USERS` | Only data for the specified users will be included in the report. Multiple values should be separated by commas. Example: `dev1, dev2` | - | +| `EXCLUDE_USERS` | Data for the specified users will be excluded from the report. Multiple values should be separated by commas. Example: `dev1, dev2` | - | | `EXECUTION_OUTCOME` | This parameter allows you to specify the format in which you wish to receive the report. Options include creating a new issue, updating an existing one, obtaining markdown, or JSON. Markdown and JSON will be available in outputs. Can take mulitple values separated by commas: `new-issue`, `markdown`, `collection`, `existing-issue`. This parameter is **required** Example: `existing-issue` | `new-issue` | | `ISSUE_NUMBER` | Issue number to update. Add `existing-issue` to `EXECUTION_OUTCOME` for updating existing issue. The specified issue must already exist at the time the action is executed. This parameter is mandatory if the `EXECUTION_OUTCOME` input includes `existing-issue` value | - | | `ALLOW_ANALYTICS` | Allows sending non-sensitive inputs to mixpanel for better understanding user's needs. Set the value to `false` to disable sending action parameter data | `true` | diff --git a/action.yml b/action.yml index 773eebd..8857327 100644 --- a/action.yml +++ b/action.yml @@ -96,6 +96,12 @@ inputs: EXCLUDE_LABELS: description: "Excludes PRs with mentioned labels. Values should be separated by comma" required: false + INCLUDE_USERS: + description: "Only data for the specified users will be included in the report. Multiple values should be separated by commas" + required: false + EXCLUDE_USERS: + description: "Data for the specified users will be excluded from the report. Multiple values should be separated by commas" + required: false TIMEZONE: description: "Timezone to use in action" required: false diff --git a/build/index.js b/build/index.js index bbd7093..d52bc86 100644 --- a/build/index.js +++ b/build/index.js @@ -428,7 +428,9 @@ const collectData = (data, teams) => { (0, utils_1.prepareRequestedReviews)(reviewRequests, collection, dateKey, teams); ["total", userKey, ...(teams[userKey] || [])].forEach((key) => { ["total", dateKey].forEach((innerKey) => { - (0, set_1.default)(collection, [key, innerKey], (0, utils_1.preparePullRequestInfo)(pullRequest, (0, get_1.default)(collection, [key, innerKey], {}))); + if ((0, calculations_1.checkUserInclusive)(userKey)) { + (0, set_1.default)(collection, [key, innerKey], (0, utils_1.preparePullRequestInfo)(pullRequest, (0, get_1.default)(collection, [key, innerKey], {}))); + } (0, set_1.default)(collection, [key, innerKey], (0, utils_1.preparePullRequestTimeline)(pullRequest, reviews, reviewRequests?.[0], statuses, (0, get_1.default)(collection, [key, innerKey], {}))); }); }); @@ -750,6 +752,32 @@ const calcWeekendMinutes = (firstDate, secondDate, holidays) => { exports.calcWeekendMinutes = calcWeekendMinutes; +/***/ }), + +/***/ 50477: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.checkUserInclusive = void 0; +const utils_1 = __nccwpck_require__(41002); +const checkUserInclusive = (name) => { + if ((0, utils_1.getMultipleValuesInput)("EXCLUDE_USERS").length === 0 && + (0, utils_1.getMultipleValuesInput)("INCLUDE_USERS").length === 0) { + return true; + } + if ((0, utils_1.getMultipleValuesInput)("EXCLUDE_USERS").length > 0 && + (0, utils_1.getMultipleValuesInput)("EXCLUDE_USERS").includes(name)) { + return false; + } + return (0, utils_1.getMultipleValuesInput)("INCLUDE_USERS").length > 0 + ? (0, utils_1.getMultipleValuesInput)("INCLUDE_USERS").includes(name) + : true; +}; +exports.checkUserInclusive = checkUserInclusive; + + /***/ }), /***/ 994: @@ -792,9 +820,13 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getApproveTime = void 0; const date_fns_1 = __nccwpck_require__(73314); const constants_1 = __nccwpck_require__(95354); +const checkUserInclusive_1 = __nccwpck_require__(50477); const getApproveTime = (reviews) => { const statuses = Object.values(reviews?.reduce((acc, review) => { const user = review.user?.login || constants_1.invalidUserLogin; + if (!(0, checkUserInclusive_1.checkUserInclusive)(user)) { + return acc; + } const statusesEntries = Object.keys(acc); const isApproved = statusesEntries.some((user) => acc[user].state === "approved") && !statusesEntries.some((user) => acc[user].state === "changes_requested") && @@ -861,10 +893,14 @@ exports.getPullRequestSize = getPullRequestSize; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getResponses = void 0; const constants_1 = __nccwpck_require__(95354); +const checkUserInclusive_1 = __nccwpck_require__(50477); const getResponses = (events = []) => { return events?.reduce((acc, event) => { if (event.event === constants_1.reviewRequestedTimelineEvent) { const user = event.requested_reviewer?.login || constants_1.invalidUserLogin; + if (!(0, checkUserInclusive_1.checkUserInclusive)(user)) { + return acc; + } return { ...acc, [user]: [...(acc?.[user] || []), [event?.created_at]], @@ -872,6 +908,9 @@ const getResponses = (events = []) => { } if (event.event === constants_1.reviewedTimelineEvent) { const user = event.user?.login || constants_1.invalidUserLogin; + if (!(0, checkUserInclusive_1.checkUserInclusive)(user)) { + return acc; + } return { ...acc, [user]: acc[user]?.map((el, index, arr) => index === arr.length - 1 && el.length < 2 @@ -881,6 +920,9 @@ const getResponses = (events = []) => { } if (event.event === constants_1.reviewRequestRemoved) { const user = event.requested_reviewer?.login || constants_1.invalidUserLogin; + if (!(0, checkUserInclusive_1.checkUserInclusive)(user)) { + return acc; + } return { ...acc, [user]: acc[user].map((el) => [ @@ -903,7 +945,7 @@ exports.getResponses = getResponses; "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.deletionCoefficient = exports.getResponses = exports.calcDraftTime = exports.getPullRequestSize = exports.calcAverageValue = exports.calcDifferenceInMinutes = exports.calcMedianValue = exports.calcNonWorkingHours = exports.calcWeekendMinutes = exports.getApproveTime = exports.calcPercentileValue = exports.calcIntervals = exports.prepareIntervals = void 0; +exports.checkUserInclusive = exports.deletionCoefficient = exports.getResponses = exports.calcDraftTime = exports.getPullRequestSize = exports.calcAverageValue = exports.calcDifferenceInMinutes = exports.calcMedianValue = exports.calcNonWorkingHours = exports.calcWeekendMinutes = exports.getApproveTime = exports.calcPercentileValue = exports.calcIntervals = exports.prepareIntervals = void 0; var prepareIntervals_1 = __nccwpck_require__(22723); Object.defineProperty(exports, "prepareIntervals", ({ enumerable: true, get: function () { return prepareIntervals_1.prepareIntervals; } })); var calcIntervals_1 = __nccwpck_require__(2414); @@ -930,6 +972,8 @@ var getResponses_1 = __nccwpck_require__(50779); Object.defineProperty(exports, "getResponses", ({ enumerable: true, get: function () { return getResponses_1.getResponses; } })); var constants_1 = __nccwpck_require__(51666); Object.defineProperty(exports, "deletionCoefficient", ({ enumerable: true, get: function () { return constants_1.deletionCoefficient; } })); +var checkUserInclusive_1 = __nccwpck_require__(50477); +Object.defineProperty(exports, "checkUserInclusive", ({ enumerable: true, get: function () { return checkUserInclusive_1.checkUserInclusive; } })); /***/ }), @@ -1052,6 +1096,7 @@ exports.prepareActionsTime = void 0; const date_fns_1 = __nccwpck_require__(73314); const lodash_1 = __nccwpck_require__(90250); const constants_1 = __nccwpck_require__(95354); +const calculations_1 = __nccwpck_require__(16576); const prepareActionsTime = (pullRequest, events = [], collection) => { const openingHour = pullRequest?.created_at ? (0, date_fns_1.getHours)((0, date_fns_1.parseISO)(pullRequest?.created_at)) @@ -1065,41 +1110,25 @@ const prepareActionsTime = (pullRequest, events = [], collection) => { const submitHour = el?.submitted_at ? (0, date_fns_1.getHours)((0, date_fns_1.parseISO)(el?.submitted_at)) : -1; - if (submitHour !== -1) { + const user = el?.user?.login || constants_1.invalidUserLogin; + if (submitHour !== -1 && (0, calculations_1.checkUserInclusive)(user)) { const keys = ["total", "total", "actionsTime", submitHour, el.state]; (0, lodash_1.set)(collection, keys, (0, lodash_1.get)(collection, keys, 0) + 1); - const userKeys = [ - el?.user?.login || constants_1.invalidUserLogin, - "total", - "actionsTime", - submitHour, - el.state, - ]; + const userKeys = [user, "total", "actionsTime", submitHour, el.state]; (0, lodash_1.set)(collection, userKeys, (0, lodash_1.get)(collection, userKeys, 0) + 1); } }); - if (openingHour !== -1) { + const prAuthor = pullRequest?.user?.login || constants_1.invalidUserLogin; + if (openingHour !== -1 && (0, calculations_1.checkUserInclusive)(prAuthor)) { const keys = ["total", "total", "actionsTime", openingHour, "opened"]; (0, lodash_1.set)(collection, keys, (0, lodash_1.get)(collection, keys, 0) + 1); - const userKeys = [ - pullRequest?.user.login || constants_1.invalidUserLogin, - "total", - "actionsTime", - openingHour, - "opened", - ]; + const userKeys = [prAuthor, "total", "actionsTime", openingHour, "opened"]; (0, lodash_1.set)(collection, userKeys, (0, lodash_1.get)(collection, userKeys, 0) + 1); } - if (mergingHour !== -1) { + if (mergingHour !== -1 && (0, calculations_1.checkUserInclusive)(prAuthor)) { const keys = ["total", "total", "actionsTime", mergingHour, "merged"]; (0, lodash_1.set)(collection, keys, (0, lodash_1.get)(collection, keys, 0) + 1); - const userKeys = [ - pullRequest?.user.login || constants_1.invalidUserLogin, - "total", - "actionsTime", - mergingHour, - "merged", - ]; + const userKeys = [prAuthor, "total", "actionsTime", mergingHour, "merged"]; (0, lodash_1.set)(collection, userKeys, (0, lodash_1.get)(collection, userKeys, 0) + 1); } }; @@ -1109,12 +1138,13 @@ exports.prepareActionsTime = prepareActionsTime; /***/ }), /***/ 15278: -/***/ ((__unused_webpack_module, exports) => { +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.prepareConductedReviews = void 0; +const calculations_1 = __nccwpck_require__(16576); const prepareConductedReviews = (pullRequestLogin, pullRequestReviews = [], collection, pullRequestSize, teams) => { const reviewsConducted = { ...(collection?.reviewsConducted || {}), @@ -1123,6 +1153,8 @@ const prepareConductedReviews = (pullRequestLogin, pullRequestReviews = [], coll return { ...acc, [review.state]: 1, total: 1 }; }, {}) || {}); [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach((key) => { + if (!(0, calculations_1.checkUserInclusive)(key)) + return; const statusesReviewsStats = statuses.reduce((acc, status) => { return { ...acc, @@ -1162,108 +1194,91 @@ const set_1 = __importDefault(__nccwpck_require__(82900)); const get_1 = __importDefault(__nccwpck_require__(56908)); const constants_1 = __nccwpck_require__(95354); const getDiscussionType_1 = __nccwpck_require__(49575); +const calculations_1 = __nccwpck_require__(16576); const prepareDiscussions = (comments, collection, index, dateKey, pullRequestLogin, teams) => { - const reviewComments = comments[index]?.filter((comment) => pullRequestLogin !== comment.user?.login); + const reviewComments = comments[index]?.filter((comment) => pullRequestLogin !== (comment.user?.login || constants_1.invalidUserLogin) && + (0, calculations_1.checkUserInclusive)(comment.user?.login || constants_1.invalidUserLogin)); const discussions = comments[index]?.filter((comment) => { const userLogin = comment.user?.login || constants_1.invalidUserLogin; - return !comment.in_reply_to_id && pullRequestLogin !== userLogin; + return (!comment.in_reply_to_id && + pullRequestLogin !== userLogin && + (0, calculations_1.checkUserInclusive)(userLogin)); }); ["total", dateKey].forEach((key) => { discussions?.forEach((discussion) => { const userLogin = discussion.user?.login || constants_1.invalidUserLogin; (0, getDiscussionType_1.getDiscussionType)(discussion.body).forEach((type) => { - [userLogin, ...(teams[userLogin] || [])].forEach((userKey) => { - (0, set_1.default)(collection, [userKey, key, "discussionsTypes", type], { - ...(0, get_1.default)(collection, [userKey, key, "discussionsTypes", type], {}), - conducted: { - total: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "total", - ], 0) + 1, - agreed: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "agreed", - ], 0) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "disagreed", - ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), - }, - }); - }); - [pullRequestLogin, ...(teams[pullRequestLogin] || [])].forEach((userKey) => { - (0, set_1.default)(collection, [userKey, key, "discussionsTypes", type], { - ...(0, get_1.default)(collection, [userKey, key, "discussionsTypes", type], {}), - received: { - total: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "received", - "total", - ], 0) + 1, - agreed: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "received", - "agreed", - ], 0) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: (0, get_1.default)(collection, [ - userKey, - key, - "discussionsTypes", - type, - "received", - "disagreed", - ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), - }, - }); + [userLogin, ...(teams[userLogin] || []), "total"].forEach((userKey) => { + if ((0, calculations_1.checkUserInclusive)(userLogin)) { + (0, set_1.default)(collection, [userKey, key, "discussionsTypes", type], { + ...(0, get_1.default)(collection, [userKey, key, "discussionsTypes", type], {}), + conducted: { + total: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "conducted", + "total", + ], 0) + 1, + agreed: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "conducted", + "agreed", + ], 0) + (discussion.reactions?.["+1"] ? 1 : 0), + disagreed: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "conducted", + "disagreed", + ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), + }, + }); + } }); - (0, set_1.default)(collection, ["total", key, "discussionsTypes", type], { - ...(0, get_1.default)(collection, ["total", key, "discussionsTypes", type], {}), - conducted: { - total: (0, get_1.default)(collection, ["total", key, "discussionsTypes", type, "conducted", "total"], 0) + 1, - agreed: (0, get_1.default)(collection, ["total", key, "discussionsTypes", type, "conducted", "agreed"], 0) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: (0, get_1.default)(collection, [ - "total", - key, - "discussionsTypes", - type, - "conducted", - "disagreed", - ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), - }, - received: { - total: (0, get_1.default)(collection, ["total", key, "discussionsTypes", type, "received", "total"], 0) + 1, - agreed: (0, get_1.default)(collection, ["total", key, "discussionsTypes", type, "received", "agreed"], 0) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: (0, get_1.default)(collection, [ - "total", - key, - "discussionsTypes", - type, - "received", - "disagreed", - ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), - }, + [pullRequestLogin, ...(teams[pullRequestLogin] || []), "total"].forEach((userKey) => { + if ((0, calculations_1.checkUserInclusive)(userLogin)) { + (0, set_1.default)(collection, [userKey, key, "discussionsTypes", type], { + ...(0, get_1.default)(collection, [userKey, key, "discussionsTypes", type], {}), + received: { + total: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "received", + "total", + ], 0) + 1, + agreed: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "received", + "agreed", + ], 0) + (discussion.reactions?.["+1"] ? 1 : 0), + disagreed: (0, get_1.default)(collection, [ + userKey, + key, + "discussionsTypes", + type, + "received", + "disagreed", + ], 0) + (discussion.reactions?.["-1"] ? 1 : 0), + }, + }); + } }); }); }); - comments[index]?.forEach((comment) => { + comments[index] + ?.filter((comment) => (0, calculations_1.checkUserInclusive)(comment.user?.login || constants_1.invalidUserLogin)) + .forEach((comment) => { const userLogin = comment.user?.login || constants_1.invalidUserLogin; if (pullRequestLogin !== userLogin) { [userLogin, "total", ...(teams[userLogin] || [])].forEach((userKey) => { @@ -1271,7 +1286,7 @@ const prepareDiscussions = (comments, collection, index, dateKey, pullRequestLog }); } }); - if (pullRequestLogin) { + if (pullRequestLogin && (0, calculations_1.checkUserInclusive)(pullRequestLogin)) { [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach((userKey) => { (0, set_1.default)(collection, [userKey, key, "reviewComments"], (reviewComments?.length || 0) + (0, get_1.default)(collection, [userKey, key, "reviewComments"], 0)); @@ -1294,14 +1309,16 @@ const prepareDiscussions = (comments, collection, index, dateKey, pullRequestLog const agreedDiscussions = discussions?.filter((discussion) => discussion.reactions?.["+1"]); const disagreedDiscussions = discussions?.filter((discussion) => discussion.reactions?.["-1"]); [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach((userKey) => { - (0, set_1.default)(collection, [userKey, key, "discussions"], { - ...(0, get_1.default)(collection, [userKey, key, "discussions"], {}), - received: { - total: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "total"], 0) + (discussions?.length || 0), - agreed: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "agreed"], 0) + (agreedDiscussions?.length || 0), - disagreed: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "disagreed"], 0) + (disagreedDiscussions?.length || 0), - }, - }); + if ((0, calculations_1.checkUserInclusive)(pullRequestLogin)) { + (0, set_1.default)(collection, [userKey, key, "discussions"], { + ...(0, get_1.default)(collection, [userKey, key, "discussions"], {}), + received: { + total: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "total"], 0) + (discussions?.length || 0), + agreed: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "agreed"], 0) + (agreedDiscussions?.length || 0), + disagreed: (0, get_1.default)(collection, [userKey, key, "discussions", "received", "disagreed"], 0) + (disagreedDiscussions?.length || 0), + }, + }); + } }); } }); @@ -1447,7 +1464,11 @@ const calculations_1 = __nccwpck_require__(16576); const calcDifferenceInMinutes_1 = __nccwpck_require__(72317); const calcPRsize_1 = __nccwpck_require__(8722); const preparePullRequestTimeline = (pullRequestInfo, pullRequestReviews = [], reviewRequest, statuses = [], collection) => { - const firstReview = pullRequestReviews?.find((review) => review.user?.login !== pullRequestInfo?.user?.login); + if (!(0, calculations_1.checkUserInclusive)(pullRequestInfo?.user?.login || constants_1.invalidUserLogin)) { + return collection; + } + const firstReview = pullRequestReviews?.find((review) => review.user?.login !== pullRequestInfo?.user?.login && + (0, calculations_1.checkUserInclusive)(review.user?.login || constants_1.invalidUserLogin)); const approveTime = (0, calculations_1.getApproveTime)(pullRequestReviews); const timeToReviewRequest = (0, calcDifferenceInMinutes_1.calcDifferenceInMinutes)(pullRequestInfo?.created_at, reviewRequest?.created_at, { endOfWorkingTime: (0, utils_1.getValueAsIs)("CORE_HOURS_END"), @@ -1554,11 +1575,14 @@ exports.prepareRequestedReviews = void 0; const set_1 = __importDefault(__nccwpck_require__(82900)); const get_1 = __importDefault(__nccwpck_require__(56908)); const constants_1 = __nccwpck_require__(95354); +const calculations_1 = __nccwpck_require__(16576); const prepareRequestedReviews = (requests = [], collection, dateKey, teams) => { const requestedReviewers = requests.reduce((acc, request) => { const user = request.requested_reviewer ? request.requested_reviewer?.login || constants_1.invalidUserLogin : request.requested_team?.name || "Invalid Team"; + if (!(0, calculations_1.checkUserInclusive)(user)) + return acc; return { ...acc, [user]: 1 }; }, {}); requestedReviewers["total"] = Object.keys(requestedReviewers).length; @@ -1569,8 +1593,10 @@ const prepareRequestedReviews = (requests = [], collection, dateKey, teams) => { }); [dateKey, "total"].forEach((date) => { Object.entries({ ...requestedReviewers }).forEach(([user, value]) => { - (0, set_1.default)(collection, [user, date, "reviewRequestsConducted"], (0, get_1.default)(collection, [user, date, "reviewRequestsConducted"], 0) + - value); + if ((0, calculations_1.checkUserInclusive)(user)) { + (0, set_1.default)(collection, [user, date, "reviewRequestsConducted"], (0, get_1.default)(collection, [user, date, "reviewRequestsConducted"], 0) + + value); + } }); }); }; @@ -1611,10 +1637,12 @@ const prepareResponseTime = (events = [], pullRequest, collection, dateKey, team startOfWorkingTime: (0, utils_1.getValueAsIs)("CORE_HOURS_START"), }, (0, utils_1.getMultipleValuesInput)("HOLIDAYS"))) .filter((el) => typeof el === "number"); - (0, set_1.default)(collection, [userKey, key, "timeWaitingForRepeatedReview"], [ - ...(0, get_1.default)(collection, [userKey, key, "timeWaitingForRepeatedReview"], []), - ...awaitingResponse, - ]); + if ((0, calculations_1.checkUserInclusive)(userKey)) { + (0, set_1.default)(collection, [userKey, key, "timeWaitingForRepeatedReview"], [ + ...(0, get_1.default)(collection, [userKey, key, "timeWaitingForRepeatedReview"], []), + ...awaitingResponse, + ]); + } }); }); Object.entries(responses).forEach(([user, responses]) => { @@ -1634,25 +1662,27 @@ const prepareResponseTime = (events = [], pullRequest, collection, dateKey, team startOfWorkingTime: (0, utils_1.getValueAsIs)("CORE_HOURS_START"), }, (0, utils_1.getMultipleValuesInput)("HOLIDAYS"))); ["total", user, ...(teams[user] || [])].forEach((userKey) => { - (0, set_1.default)(collection, [userKey, key], { - ...(0, get_1.default)(collection, [userKey, key], {}), - timeFromInitialRequestToResponse: typeof timeFromInitialRequestToResponse === "number" - ? [ - ...(0, get_1.default)(collection, [userKey, key, "timeFromInitialRequestToResponse"], []), - timeFromInitialRequestToResponse, - ] - : (0, get_1.default)(collection, [userKey, key, "timeFromInitialRequestToResponse"], []), - timeFromOpenToResponse: typeof timeFromOpenToResponse === "number" - ? [ - ...(0, get_1.default)(collection, [userKey, key, "timeFromOpenToResponse"], []), - timeFromOpenToResponse, - ] - : (0, get_1.default)(collection, [userKey, key, "timeFromOpenToResponse"], []), - timeFromRepeatedRequestToResponse: [ - ...(0, get_1.default)(collection, [userKey, key, "timeFromRepeatedRequestToResponse"], []), - ...timeFromRepeatedRequestToResponse.filter((el) => typeof el === "number"), - ], - }); + if ((0, calculations_1.checkUserInclusive)(userKey)) { + (0, set_1.default)(collection, [userKey, key], { + ...(0, get_1.default)(collection, [userKey, key], {}), + timeFromInitialRequestToResponse: typeof timeFromInitialRequestToResponse === "number" + ? [ + ...(0, get_1.default)(collection, [userKey, key, "timeFromInitialRequestToResponse"], []), + timeFromInitialRequestToResponse, + ] + : (0, get_1.default)(collection, [userKey, key, "timeFromInitialRequestToResponse"], []), + timeFromOpenToResponse: typeof timeFromOpenToResponse === "number" + ? [ + ...(0, get_1.default)(collection, [userKey, key, "timeFromOpenToResponse"], []), + timeFromOpenToResponse, + ] + : (0, get_1.default)(collection, [userKey, key, "timeFromOpenToResponse"], []), + timeFromRepeatedRequestToResponse: [ + ...(0, get_1.default)(collection, [userKey, key, "timeFromRepeatedRequestToResponse"], []), + ...timeFromRepeatedRequestToResponse.filter((el) => typeof el === "number"), + ], + }); + } }); }); }); @@ -1676,24 +1706,28 @@ const set_1 = __importDefault(__nccwpck_require__(82900)); const get_1 = __importDefault(__nccwpck_require__(56908)); const constants_1 = __nccwpck_require__(95354); const prepareConductedReviews_1 = __nccwpck_require__(15278); +const calculations_1 = __nccwpck_require__(16576); const prepareReviews = (reviews = [], collection, dateKey, pullRequestLogin, pullRequestSize, teams) => { let teamNames = []; const users = Object.keys(reviews?.reduce((acc, review) => { const userLogin = review.user?.login || constants_1.invalidUserLogin; - if (userLogin !== pullRequestLogin) { + if (userLogin !== pullRequestLogin && (0, calculations_1.checkUserInclusive)(userLogin)) { const teamsNames = (teams[userLogin] || []).reduce((acc, team) => ({ ...acc, [team]: 1 }), {}); teamNames = Object.keys(teamsNames); - return { ...acc, [userLogin]: 1, ...teamsNames }; + return { ...acc, [userLogin]: 1, ...teamsNames, total: 1 }; } return acc; - }, {}) || {}).concat("total"); + }, {}) || {}); users.forEach((user) => { const userReviews = Array.isArray(reviews) && user !== "total" && !teamNames.includes(user) ? reviews?.filter((review) => { const userLogin = review.user?.login || constants_1.invalidUserLogin; - return userLogin === user; + return userLogin === user && (0, calculations_1.checkUserInclusive)(userLogin); }) - : reviews; + : reviews?.filter((review) => { + const userLogin = review.user?.login || constants_1.invalidUserLogin; + return (0, calculations_1.checkUserInclusive)(userLogin); + }); [dateKey, "total"].forEach((key) => { (0, set_1.default)(collection, [user, key], (0, prepareConductedReviews_1.prepareConductedReviews)(pullRequestLogin, userReviews, (0, get_1.default)(collection, [user, key], {}), pullRequestSize, teams)); }); @@ -2511,7 +2545,7 @@ const createMarkdown = (data, users, dates, title = "Pull Request report", refer }); if (content.join("").trim() === "") return ""; - const issueDescription = `This report based on ${data.total?.total?.closed || 0} last updated PRs. To learn more about the project and its configuration, please visit [Pull request analytics action](https://github.com/AlexSim93/pull-request-analytics-action). + const issueDescription = `To learn more about the project and its configuration, please visit [Pull request analytics action](https://github.com/AlexSim93/pull-request-analytics-action). ${(0, utils_1.createConfigParamsCode)()}`; return ` ## ${title} @@ -2875,6 +2909,8 @@ ${[ "USE_CHARTS", "INCLUDE_LABELS", "EXCLUDE_LABELS", + "INCLUDE_USERS", + "EXCLUDE_USERS", "EXECUTION_OUTCOME", "ISSUE_NUMBER", ] diff --git a/package.json b/package.json index 9294fbc..b9bb4ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pull-request-analytics-action", - "version": "4.6.2", + "version": "4.7.0", "description": "Generates detailed PR analytics reports within GitHub, focusing on review efficiency and team performance.", "main": "build/index.js", "scripts": { diff --git a/src/converters/collectData.ts b/src/converters/collectData.ts index 02d486a..d04fcaa 100644 --- a/src/converters/collectData.ts +++ b/src/converters/collectData.ts @@ -22,7 +22,7 @@ import { readyForReviewTimelineEvent, convertToDraftTimelineEvent, } from "./constants"; -import { getPullRequestSize } from "./utils/calculations"; +import { checkUserInclusive, getPullRequestSize } from "./utils/calculations"; import { getDateFormat } from "../common/utils"; export const collectData = ( @@ -68,14 +68,16 @@ export const collectData = ( ["total", userKey, ...(teams[userKey] || [])].forEach((key) => { ["total", dateKey].forEach((innerKey) => { - set( - collection, - [key, innerKey], - preparePullRequestInfo( - pullRequest, - get(collection, [key, innerKey], {}) - ) - ); + if (checkUserInclusive(userKey)) { + set( + collection, + [key, innerKey], + preparePullRequestInfo( + pullRequest, + get(collection, [key, innerKey], {}) + ) + ); + } set( collection, diff --git a/src/converters/utils/calculations/checkUserInclusive.ts b/src/converters/utils/calculations/checkUserInclusive.ts index 972fab1..c63e198 100644 --- a/src/converters/utils/calculations/checkUserInclusive.ts +++ b/src/converters/utils/calculations/checkUserInclusive.ts @@ -1,4 +1,18 @@ import { getMultipleValuesInput } from "../../../common/utils"; export const checkUserInclusive = (name: string) => { - return !getMultipleValuesInput('EXCLUDE_USERS').includes(name) ; -}; \ No newline at end of file + if ( + getMultipleValuesInput("EXCLUDE_USERS").length === 0 && + getMultipleValuesInput("INCLUDE_USERS").length === 0 + ) { + return true; + } + if ( + getMultipleValuesInput("EXCLUDE_USERS").length > 0 && + getMultipleValuesInput("EXCLUDE_USERS").includes(name) + ) { + return false; + } + return getMultipleValuesInput("INCLUDE_USERS").length > 0 + ? getMultipleValuesInput("INCLUDE_USERS").includes(name) + : true; +}; diff --git a/src/converters/utils/calculations/getApproveTime.ts b/src/converters/utils/calculations/getApproveTime.ts index 6b061ff..9d7bdc6 100644 --- a/src/converters/utils/calculations/getApproveTime.ts +++ b/src/converters/utils/calculations/getApproveTime.ts @@ -1,6 +1,7 @@ import { isBefore, parseISO } from "date-fns"; import { makeComplexRequest } from "../../../requests"; import { invalidUserLogin } from "../../constants"; +import { checkUserInclusive } from "./checkUserInclusive"; export const getApproveTime = ( reviews: Awaited>["events"][number] @@ -12,6 +13,9 @@ export const getApproveTime = ( review: any ) => { const user = review.user?.login || invalidUserLogin; + if(!checkUserInclusive(user)){ + return acc; + } const statusesEntries = Object.keys(acc) as string[]; const isApproved = statusesEntries.some((user) => acc[user].state === "approved") && diff --git a/src/converters/utils/calculations/getResponses.ts b/src/converters/utils/calculations/getResponses.ts index 0c95081..7fdf239 100644 --- a/src/converters/utils/calculations/getResponses.ts +++ b/src/converters/utils/calculations/getResponses.ts @@ -4,11 +4,15 @@ import { reviewRequestedTimelineEvent, reviewedTimelineEvent, } from "../../constants"; +import { checkUserInclusive } from "./checkUserInclusive"; export const getResponses = (events: any[] | undefined | null = []) => { return events?.reduce((acc, event) => { if (event.event === reviewRequestedTimelineEvent) { const user = event.requested_reviewer?.login || invalidUserLogin; + if (!checkUserInclusive(user)) { + return acc; + } return { ...acc, [user]: [...(acc?.[user] || []), [(event as any)?.created_at]], @@ -16,6 +20,9 @@ export const getResponses = (events: any[] | undefined | null = []) => { } if (event.event === reviewedTimelineEvent) { const user = event.user?.login || invalidUserLogin; + if (!checkUserInclusive(user)) { + return acc; + } return { ...acc, [user]: acc[user]?.map((el: any, index: number, arr: any[]) => @@ -27,6 +34,9 @@ export const getResponses = (events: any[] | undefined | null = []) => { } if (event.event === reviewRequestRemoved) { const user = event.requested_reviewer?.login || invalidUserLogin; + if (!checkUserInclusive(user)) { + return acc; + } return { ...acc, [user]: acc[user].map( diff --git a/src/converters/utils/calculations/index.ts b/src/converters/utils/calculations/index.ts index 5c1136b..79855dd 100644 --- a/src/converters/utils/calculations/index.ts +++ b/src/converters/utils/calculations/index.ts @@ -11,3 +11,4 @@ export { getPullRequestSize } from "./getPullRequestSize"; export { calcDraftTime } from "./calcDraftTime"; export { getResponses } from "./getResponses"; export { deletionCoefficient } from "./constants"; +export { checkUserInclusive } from "./checkUserInclusive"; diff --git a/src/converters/utils/prepareActionsTime.ts b/src/converters/utils/prepareActionsTime.ts index 7d29c49..f8c9ce1 100644 --- a/src/converters/utils/prepareActionsTime.ts +++ b/src/converters/utils/prepareActionsTime.ts @@ -3,6 +3,7 @@ import { makeComplexRequest } from "../../requests"; import { Collection } from "../types"; import { get, set } from "lodash"; import { invalidUserLogin, reviewedTimelineEvent } from "../constants"; +import { checkUserInclusive } from "./calculations"; export const prepareActionsTime = ( pullRequest: Awaited< @@ -24,41 +25,25 @@ export const prepareActionsTime = ( const submitHour = el?.submitted_at ? getHours(parseISO(el?.submitted_at)) : -1; - if (submitHour !== -1) { + const user = el?.user?.login || invalidUserLogin; + if (submitHour !== -1 && checkUserInclusive(user)) { const keys = ["total", "total", "actionsTime", submitHour, el.state]; set(collection, keys, (get(collection, keys, 0) as number) + 1); - const userKeys = [ - el?.user?.login || invalidUserLogin, - "total", - "actionsTime", - submitHour, - el.state, - ]; + const userKeys = [user, "total", "actionsTime", submitHour, el.state]; set(collection, userKeys, (get(collection, userKeys, 0) as number) + 1); } }); - if (openingHour !== -1) { + const prAuthor = pullRequest?.user?.login || invalidUserLogin; + if (openingHour !== -1 && checkUserInclusive(prAuthor)) { const keys = ["total", "total", "actionsTime", openingHour, "opened"]; set(collection, keys, get(collection, keys, 0) + 1); - const userKeys = [ - pullRequest?.user.login || invalidUserLogin, - "total", - "actionsTime", - openingHour, - "opened", - ]; + const userKeys = [prAuthor, "total", "actionsTime", openingHour, "opened"]; set(collection, userKeys, get(collection, userKeys, 0) + 1); } - if (mergingHour !== -1) { + if (mergingHour !== -1 && checkUserInclusive(prAuthor)) { const keys = ["total", "total", "actionsTime", mergingHour, "merged"]; set(collection, keys, get(collection, keys, 0) + 1); - const userKeys = [ - pullRequest?.user.login || invalidUserLogin, - "total", - "actionsTime", - mergingHour, - "merged", - ]; + const userKeys = [prAuthor, "total", "actionsTime", mergingHour, "merged"]; set(collection, userKeys, get(collection, userKeys, 0) + 1); } }; diff --git a/src/converters/utils/prepareConductedReviews.ts b/src/converters/utils/prepareConductedReviews.ts index 5ca7033..e1b510a 100644 --- a/src/converters/utils/prepareConductedReviews.ts +++ b/src/converters/utils/prepareConductedReviews.ts @@ -1,4 +1,5 @@ import { Collection } from "../types"; +import { checkUserInclusive } from "./calculations"; import { PullRequestSize } from "./calculations/getPullRequestSize"; export const prepareConductedReviews = ( @@ -19,6 +20,7 @@ export const prepareConductedReviews = ( [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach( (key) => { + if(!checkUserInclusive(key)) return; const statusesReviewsStats = statuses.reduce((acc, status) => { return { ...acc, diff --git a/src/converters/utils/prepareDiscussions.ts b/src/converters/utils/prepareDiscussions.ts index ff1bca7..c884e2a 100644 --- a/src/converters/utils/prepareDiscussions.ts +++ b/src/converters/utils/prepareDiscussions.ts @@ -5,6 +5,7 @@ import { makeComplexRequest } from "../../requests"; import { invalidUserLogin } from "../constants"; import { Collection } from "../types"; import { getDiscussionType } from "./getDiscussionType"; +import { checkUserInclusive } from "./calculations"; export const prepareDiscussions = ( comments: Awaited>["comments"], @@ -15,70 +16,29 @@ export const prepareDiscussions = ( teams: Record ) => { const reviewComments = comments[index]?.filter( - (comment) => pullRequestLogin !== comment.user?.login + (comment) => + pullRequestLogin !== (comment.user?.login || invalidUserLogin) && + checkUserInclusive(comment.user?.login || invalidUserLogin) ); const discussions = comments[index]?.filter((comment) => { const userLogin = comment.user?.login || invalidUserLogin; - return !comment.in_reply_to_id && pullRequestLogin !== userLogin; + return ( + !comment.in_reply_to_id && + pullRequestLogin !== userLogin && + checkUserInclusive(userLogin) + ); }); ["total", dateKey].forEach((key) => { discussions?.forEach((discussion) => { const userLogin = discussion.user?.login || invalidUserLogin; getDiscussionType(discussion.body).forEach((type) => { - [userLogin, ...(teams[userLogin] || [])].forEach((userKey) => { - set(collection, [userKey, key, "discussionsTypes", type], { - ...get(collection, [userKey, key, "discussionsTypes", type], {}), - conducted: { - total: - get( - collection, - [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "total", - ], - 0 - ) + 1, - agreed: - get( - collection, - [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "agreed", - ], - 0 - ) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: - get( - collection, - [ - userKey, - key, - "discussionsTypes", - type, - "conducted", - "disagreed", - ], - 0 - ) + (discussion.reactions?.["-1"] ? 1 : 0), - }, - }); - }); - - [pullRequestLogin, ...(teams[pullRequestLogin] || [])].forEach( - (userKey) => { + [userLogin, ...(teams[userLogin] || []), "total"].forEach((userKey) => { + if (checkUserInclusive(userLogin)) { set(collection, [userKey, key, "discussionsTypes", type], { ...get(collection, [userKey, key, "discussionsTypes", type], {}), - received: { + conducted: { total: get( collection, @@ -87,7 +47,7 @@ export const prepareDiscussions = ( key, "discussionsTypes", type, - "received", + "conducted", "total", ], 0 @@ -100,7 +60,7 @@ export const prepareDiscussions = ( key, "discussionsTypes", type, - "received", + "conducted", "agreed", ], 0 @@ -113,7 +73,7 @@ export const prepareDiscussions = ( key, "discussionsTypes", type, - "received", + "conducted", "disagreed", ], 0 @@ -121,81 +81,85 @@ export const prepareDiscussions = ( }, }); } - ); - set(collection, ["total", key, "discussionsTypes", type], { - ...get(collection, ["total", key, "discussionsTypes", type], {}), - conducted: { - total: - get( - collection, - ["total", key, "discussionsTypes", type, "conducted", "total"], - 0 - ) + 1, - agreed: - get( - collection, - ["total", key, "discussionsTypes", type, "conducted", "agreed"], - 0 - ) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: - get( - collection, - [ - "total", - key, - "discussionsTypes", - type, - "conducted", - "disagreed", - ], - 0 - ) + (discussion.reactions?.["-1"] ? 1 : 0), - }, - received: { - total: - get( - collection, - ["total", key, "discussionsTypes", type, "received", "total"], - 0 - ) + 1, - agreed: - get( - collection, - ["total", key, "discussionsTypes", type, "received", "agreed"], - 0 - ) + (discussion.reactions?.["+1"] ? 1 : 0), - disagreed: - get( - collection, - [ - "total", - key, - "discussionsTypes", - type, - "received", - "disagreed", - ], - 0 - ) + (discussion.reactions?.["-1"] ? 1 : 0), - }, }); + + [pullRequestLogin, ...(teams[pullRequestLogin] || []), "total"].forEach( + (userKey) => { + if (checkUserInclusive(userLogin)) { + set(collection, [userKey, key, "discussionsTypes", type], { + ...get( + collection, + [userKey, key, "discussionsTypes", type], + {} + ), + received: { + total: + get( + collection, + [ + userKey, + key, + "discussionsTypes", + type, + "received", + "total", + ], + 0 + ) + 1, + agreed: + get( + collection, + [ + userKey, + key, + "discussionsTypes", + type, + "received", + "agreed", + ], + 0 + ) + (discussion.reactions?.["+1"] ? 1 : 0), + disagreed: + get( + collection, + [ + userKey, + key, + "discussionsTypes", + type, + "received", + "disagreed", + ], + 0 + ) + (discussion.reactions?.["-1"] ? 1 : 0), + }, + }); + } + } + ); }); }); - comments[index]?.forEach((comment) => { - const userLogin = comment.user?.login || invalidUserLogin; - if (pullRequestLogin !== userLogin) { - [userLogin, "total", ...(teams[userLogin] || [])].forEach((userKey) => { - set( - collection, - [userKey, key, "commentsConducted"], - get(collection, [userKey, key, "commentsConducted"], 0) + 1 + comments[index] + ?.filter((comment) => + checkUserInclusive(comment.user?.login || invalidUserLogin) + ) + .forEach((comment) => { + const userLogin = comment.user?.login || invalidUserLogin; + if (pullRequestLogin !== userLogin) { + [userLogin, "total", ...(teams[userLogin] || [])].forEach( + (userKey) => { + set( + collection, + [userKey, key, "commentsConducted"], + get(collection, [userKey, key, "commentsConducted"], 0) + 1 + ); + } ); - }); - } - }); + } + }); - if (pullRequestLogin) { + if (pullRequestLogin && checkUserInclusive(pullRequestLogin)) { [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach( (userKey) => { set( @@ -247,29 +211,31 @@ export const prepareDiscussions = ( [pullRequestLogin, "total", ...(teams[pullRequestLogin] || [])].forEach( (userKey) => { - set(collection, [userKey, key, "discussions"], { - ...get(collection, [userKey, key, "discussions"], {}), - received: { - total: - get( - collection, - [userKey, key, "discussions", "received", "total"], - 0 - ) + (discussions?.length || 0), - agreed: - get( - collection, - [userKey, key, "discussions", "received", "agreed"], - 0 - ) + (agreedDiscussions?.length || 0), - disagreed: - get( - collection, - [userKey, key, "discussions", "received", "disagreed"], - 0 - ) + (disagreedDiscussions?.length || 0), - }, - }); + if (checkUserInclusive(pullRequestLogin)) { + set(collection, [userKey, key, "discussions"], { + ...get(collection, [userKey, key, "discussions"], {}), + received: { + total: + get( + collection, + [userKey, key, "discussions", "received", "total"], + 0 + ) + (discussions?.length || 0), + agreed: + get( + collection, + [userKey, key, "discussions", "received", "agreed"], + 0 + ) + (agreedDiscussions?.length || 0), + disagreed: + get( + collection, + [userKey, key, "discussions", "received", "disagreed"], + 0 + ) + (disagreedDiscussions?.length || 0), + }, + }); + } } ); } diff --git a/src/converters/utils/preparePullRequestTimeline.ts b/src/converters/utils/preparePullRequestTimeline.ts index 8a67a3f..86e655a 100644 --- a/src/converters/utils/preparePullRequestTimeline.ts +++ b/src/converters/utils/preparePullRequestTimeline.ts @@ -4,6 +4,7 @@ import { invalidUserLogin } from "../constants"; import { Collection } from "../types"; import { calcDraftTime, + checkUserInclusive, getApproveTime, getPullRequestSize, } from "./calculations"; @@ -19,8 +20,13 @@ export const preparePullRequestTimeline = ( statuses: any[] | undefined = [], collection: Collection ) => { + if (!checkUserInclusive(pullRequestInfo?.user?.login || invalidUserLogin)) { + return collection; + } const firstReview = pullRequestReviews?.find( - (review) => review.user?.login !== pullRequestInfo?.user?.login + (review) => + review.user?.login !== pullRequestInfo?.user?.login && + checkUserInclusive(review.user?.login || invalidUserLogin) ); const approveTime = getApproveTime(pullRequestReviews); diff --git a/src/converters/utils/prepareRequestedReviews.ts b/src/converters/utils/prepareRequestedReviews.ts index 45b8423..29491f1 100644 --- a/src/converters/utils/prepareRequestedReviews.ts +++ b/src/converters/utils/prepareRequestedReviews.ts @@ -3,6 +3,7 @@ import get from "lodash/get"; import { invalidUserLogin } from "../constants"; import { Collection } from "../types"; +import { checkUserInclusive } from "./calculations"; export const prepareRequestedReviews = ( requests: any[] = [], @@ -14,6 +15,7 @@ export const prepareRequestedReviews = ( const user = request.requested_reviewer ? request.requested_reviewer?.login || invalidUserLogin : request.requested_team?.name || "Invalid Team"; + if(!checkUserInclusive(user)) return acc; return { ...acc, [user]: 1 }; }, {}); @@ -27,12 +29,14 @@ export const prepareRequestedReviews = ( [dateKey, "total"].forEach((date) => { Object.entries({ ...requestedReviewers }).forEach(([user, value]) => { - set( - collection, - [user, date, "reviewRequestsConducted"], - get(collection, [user, date, "reviewRequestsConducted"], 0) + - (value as number) - ); + if (checkUserInclusive(user)) { + set( + collection, + [user, date, "reviewRequestsConducted"], + get(collection, [user, date, "reviewRequestsConducted"], 0) + + (value as number) + ); + } }); }); }; diff --git a/src/converters/utils/prepareResponseTime.ts b/src/converters/utils/prepareResponseTime.ts index f9fe03d..3e05df0 100644 --- a/src/converters/utils/prepareResponseTime.ts +++ b/src/converters/utils/prepareResponseTime.ts @@ -4,7 +4,11 @@ import get from "lodash/get"; import { getMultipleValuesInput, getValueAsIs } from "../../common/utils"; import { makeComplexRequest } from "../../requests"; import { Collection } from "../types"; -import { calcDifferenceInMinutes, getResponses } from "./calculations"; +import { + calcDifferenceInMinutes, + checkUserInclusive, + getResponses, +} from "./calculations"; import { invalidUserLogin } from "../constants"; export const prepareResponseTime = ( @@ -40,18 +44,20 @@ export const prepareResponseTime = ( ) ) .filter((el) => typeof el === "number") as number[]; - set( - collection, - [userKey, key, "timeWaitingForRepeatedReview"], - [ - ...get( - collection, - [userKey, key, "timeWaitingForRepeatedReview"], - [] - ), - ...awaitingResponse, - ] - ); + if (checkUserInclusive(userKey)) { + set( + collection, + [userKey, key, "timeWaitingForRepeatedReview"], + [ + ...get( + collection, + [userKey, key, "timeWaitingForRepeatedReview"], + [] + ), + ...awaitingResponse, + ] + ); + } }); }); @@ -93,45 +99,51 @@ export const prepareResponseTime = ( ); ["total", user, ...(teams[user] || [])].forEach((userKey) => { - set(collection, [userKey, key], { - ...get(collection, [userKey, key], {}), - timeFromInitialRequestToResponse: - typeof timeFromInitialRequestToResponse === "number" - ? [ - ...get( + if (checkUserInclusive(userKey)) { + set(collection, [userKey, key], { + ...get(collection, [userKey, key], {}), + timeFromInitialRequestToResponse: + typeof timeFromInitialRequestToResponse === "number" + ? [ + ...get( + collection, + [userKey, key, "timeFromInitialRequestToResponse"], + [] + ), + timeFromInitialRequestToResponse, + ] + : get( collection, [userKey, key, "timeFromInitialRequestToResponse"], [] ), - timeFromInitialRequestToResponse, - ] - : get( - collection, - [userKey, key, "timeFromInitialRequestToResponse"], - [] - ), - timeFromOpenToResponse: - typeof timeFromOpenToResponse === "number" - ? [ - ...get( + timeFromOpenToResponse: + typeof timeFromOpenToResponse === "number" + ? [ + ...get( + collection, + [userKey, key, "timeFromOpenToResponse"], + [] + ), + timeFromOpenToResponse, + ] + : get( collection, [userKey, key, "timeFromOpenToResponse"], [] ), - timeFromOpenToResponse, - ] - : get(collection, [userKey, key, "timeFromOpenToResponse"], []), - timeFromRepeatedRequestToResponse: [ - ...get( - collection, - [userKey, key, "timeFromRepeatedRequestToResponse"], - [] - ), - ...(timeFromRepeatedRequestToResponse.filter( - (el) => typeof el === "number" - ) as number[]), - ], - }); + timeFromRepeatedRequestToResponse: [ + ...get( + collection, + [userKey, key, "timeFromRepeatedRequestToResponse"], + [] + ), + ...(timeFromRepeatedRequestToResponse.filter( + (el) => typeof el === "number" + ) as number[]), + ], + }); + } }); }); } diff --git a/src/converters/utils/prepareReviews.ts b/src/converters/utils/prepareReviews.ts index 1b1cc67..927e107 100644 --- a/src/converters/utils/prepareReviews.ts +++ b/src/converters/utils/prepareReviews.ts @@ -5,6 +5,7 @@ import { invalidUserLogin } from "../constants"; import { Collection } from "../types"; import { PullRequestSize } from "./calculations/getPullRequestSize"; import { prepareConductedReviews } from "./prepareConductedReviews"; +import { checkUserInclusive } from "./calculations"; export const prepareReviews = ( reviews: any[] = [], @@ -18,26 +19,29 @@ export const prepareReviews = ( const users = Object.keys( reviews?.reduce((acc, review) => { const userLogin = review.user?.login || invalidUserLogin; - if (userLogin !== pullRequestLogin) { + if (userLogin !== pullRequestLogin && checkUserInclusive(userLogin)) { const teamsNames = (teams[userLogin] || []).reduce( (acc, team) => ({ ...acc, [team]: 1 }), {} ); teamNames = Object.keys(teamsNames); - return { ...acc, [userLogin]: 1, ...teamsNames }; + return { ...acc, [userLogin]: 1, ...teamsNames, total: 1 }; } return acc; }, {}) || {} - ).concat("total"); + ); users.forEach((user) => { const userReviews = Array.isArray(reviews) && user !== "total" && !teamNames.includes(user) ? reviews?.filter((review) => { const userLogin = review.user?.login || invalidUserLogin; - return userLogin === user; + return userLogin === user && checkUserInclusive(userLogin); }) - : reviews; + : reviews?.filter((review) => { + const userLogin = review.user?.login || invalidUserLogin; + return checkUserInclusive(userLogin); + }); [dateKey, "total"].forEach((key) => { set( collection, diff --git a/src/view/createMarkdown.ts b/src/view/createMarkdown.ts index c87c3a9..8a2aa22 100644 --- a/src/view/createMarkdown.ts +++ b/src/view/createMarkdown.ts @@ -40,9 +40,7 @@ export const createMarkdown = ( if (content.join("").trim() === "") return ""; - const issueDescription = `This report based on ${ - data.total?.total?.closed || 0 - } last updated PRs. To learn more about the project and its configuration, please visit [Pull request analytics action](https://github.com/AlexSim93/pull-request-analytics-action). + const issueDescription = `To learn more about the project and its configuration, please visit [Pull request analytics action](https://github.com/AlexSim93/pull-request-analytics-action). ${createConfigParamsCode()}`; return ` diff --git a/src/view/utils/createConfigParamsCode.ts b/src/view/utils/createConfigParamsCode.ts index ca9746c..ac26f69 100644 --- a/src/view/utils/createConfigParamsCode.ts +++ b/src/view/utils/createConfigParamsCode.ts @@ -35,6 +35,8 @@ ${[ "USE_CHARTS", "INCLUDE_LABELS", "EXCLUDE_LABELS", + "INCLUDE_USERS", + "EXCLUDE_USERS", "EXECUTION_OUTCOME", "ISSUE_NUMBER", ] From 3c62bcca15fa00a884d127ef7906f4407a3550e3 Mon Sep 17 00:00:00 2001 From: Aleksei Simatov Date: Sat, 14 Jun 2025 13:44:55 +0700 Subject: [PATCH 2/2] Fix: Correct unreviewed and unapproved calculations --- build/index.js | 4 ++-- src/converters/utils/preparePullRequestTimeline.ts | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/build/index.js b/build/index.js index d52bc86..0aa5a7b 100644 --- a/build/index.js +++ b/build/index.js @@ -1509,10 +1509,10 @@ const preparePullRequestTimeline = (pullRequestInfo, pullRequestReviews = [], re timeInDraft: typeof timeInDraft === "number" ? [...(collection?.timeInDraft || []), timeInDraft] : collection.timeInDraft, - unreviewed: timeToReview + unreviewed: timeToReview !== null ? collection?.unreviewed || 0 : (collection?.unreviewed || 0) + 1, - unapproved: timeToApprove + unapproved: timeToApprove !== null ? collection?.unapproved || 0 : (collection?.unapproved || 0) + 1, sizes: { diff --git a/src/converters/utils/preparePullRequestTimeline.ts b/src/converters/utils/preparePullRequestTimeline.ts index 86e655a..439d0a5 100644 --- a/src/converters/utils/preparePullRequestTimeline.ts +++ b/src/converters/utils/preparePullRequestTimeline.ts @@ -116,12 +116,14 @@ export const preparePullRequestTimeline = ( typeof timeInDraft === "number" ? [...(collection?.timeInDraft || []), timeInDraft] : collection.timeInDraft, - unreviewed: timeToReview - ? collection?.unreviewed || 0 - : (collection?.unreviewed || 0) + 1, - unapproved: timeToApprove - ? collection?.unapproved || 0 - : (collection?.unapproved || 0) + 1, + unreviewed: + timeToReview !== null + ? collection?.unreviewed || 0 + : (collection?.unreviewed || 0) + 1, + unapproved: + timeToApprove !== null + ? collection?.unapproved || 0 + : (collection?.unapproved || 0) + 1, sizes: { ...(collection.sizes || {}), [pullRequestSize]: {