From c7896e234cb7d522a03cec2ba6ddd536696db69b Mon Sep 17 00:00:00 2001 From: DevPanther Date: Sat, 30 Sep 2023 04:59:39 +0100 Subject: [PATCH 1/2] chore: setting up debug table --- src/handlers/comment/handlers/index.ts | 1 + src/handlers/payout/action.ts | 1 + src/handlers/payout/post.ts | 43 ++++++++++++++++++-------- src/helpers/comment.ts | 26 ++++++++++++++++ 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/handlers/comment/handlers/index.ts b/src/handlers/comment/handlers/index.ts index 00890f271..7e2cb6716 100644 --- a/src/handlers/comment/handlers/index.ts +++ b/src/handlers/comment/handlers/index.ts @@ -60,6 +60,7 @@ export interface RewardsResponse { penaltyAmount: BigNumber; user: string; userId: number; + debug: Record; }[]; fallbackReward?: Record; } diff --git a/src/handlers/payout/action.ts b/src/handlers/payout/action.ts index 40a8431c5..e095da752 100644 --- a/src/handlers/payout/action.ts +++ b/src/handlers/payout/action.ts @@ -324,6 +324,7 @@ export const calculateIssueAssigneeReward = async (incentivesCalculation: Incent account: account || "0x", user: "", userId: incentivesCalculation.assignee.id, + debug: {}, }, ], }; diff --git a/src/handlers/payout/post.ts b/src/handlers/payout/post.ts index aedddadde..bb2e715c5 100644 --- a/src/handlers/payout/post.ts +++ b/src/handlers/payout/post.ts @@ -67,25 +67,25 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince const fallbackReward: Record = {}; // array of awaiting permits to generate - const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber }[] = []; + const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber; debug: Record }[] = []; for (const user of Object.keys(issueCommentsByUser)) { const commentsByUser = issueCommentsByUser[user]; const commentsByNode = await parseComments(commentsByUser.comments, ItemsToExclude); const rewardValue = calculateRewardValue(commentsByNode, calculateIncentives.incentives); - if (rewardValue.equals(0)) { + if (rewardValue.sum.equals(0)) { logger.info(`Skipping to generate a permit url because the reward value is 0. user: ${user}`); continue; } logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); const account = await getWalletAddress(user); - const priceInEth = rewardValue.mul(calculateIncentives.baseMultiplier); + const priceInEth = rewardValue.sum.mul(calculateIncentives.baseMultiplier); if (priceInEth.gt(calculateIncentives.paymentPermitMaxPrice)) { logger.info(`Skipping comment reward for user ${user} because reward is higher than payment permit max price`); continue; } if (account) { - reward.push({ account, priceInEth, userId: commentsByUser.id, user, penaltyAmount: BigNumber.from(0) }); + reward.push({ account, priceInEth, userId: commentsByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.rewardSumByType }); } else { fallbackReward[user] = priceInEth; } @@ -147,6 +147,7 @@ export const calculateIssueCreatorReward = async (incentivesCalculation: Incenti userId: creator.id, user: "", penaltyAmount: BigNumber.from(0), + debug: {}, }, ], }; @@ -212,7 +213,7 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I logger.info(`calculatePullRequestReviewsReward: Filtering by the user type done. commentsByUser: ${JSON.stringify(prReviewsByUser)}`); // array of awaiting permits to generate - const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber }[] = []; + const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber; debug: Record }[] = []; // The mapping between gh handle and amount in ETH const fallbackReward: Record = {}; @@ -221,20 +222,20 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I const commentByUser = prReviewsByUser[user]; const commentsByNode = await parseComments(commentByUser.comments, ItemsToExclude); const rewardValue = calculateRewardValue(commentsByNode, incentivesCalculation.incentives); - if (rewardValue.equals(0)) { + if (rewardValue.sum.equals(0)) { logger.info(`calculatePullRequestReviewsReward: Skipping to generate a permit url because the reward value is 0. user: ${user}`); continue; } logger.info(`calculatePullRequestReviewsReward: Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); const account = await getWalletAddress(user); - const priceInEth = rewardValue.mul(incentivesCalculation.baseMultiplier); + const priceInEth = rewardValue.sum.mul(incentivesCalculation.baseMultiplier); if (priceInEth.gt(incentivesCalculation.paymentPermitMaxPrice)) { logger.info(`calculatePullRequestReviewsReward: Skipping comment reward for user ${user} because reward is higher than payment permit max price`); continue; } if (account) { - reward.push({ account, priceInEth, userId: commentByUser.id, user, penaltyAmount: BigNumber.from(0) }); + reward.push({ account, priceInEth, userId: commentByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.rewardSumByType }); } else { fallbackReward[user] = priceInEth; } @@ -256,13 +257,13 @@ const generatePermitForComments = async ( const logger = getLogger(); const commentsByNode = await parseComments(comments, ItemsToExclude); const rewardValue = calculateRewardValue(commentsByNode, incentives); - if (rewardValue.equals(0)) { + if (rewardValue.sum.equals(0)) { logger.info(`No reward for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); return; } logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); const account = await getWalletAddress(user); - const amountInETH = rewardValue.mul(multiplier); + const amountInETH = rewardValue.sum.mul(multiplier); if (amountInETH.gt(paymentPermitMaxPrice)) { logger.info(`Skipping issue creator reward for user ${user} because reward is higher than payment permit max price`); return; @@ -280,18 +281,27 @@ const generatePermitForComments = async ( * @param incentives - The basic price table for reward calculation * @returns - The reward value */ -const calculateRewardValue = (comments: Record, incentives: Incentives): Decimal => { +const calculateRewardValue = (comments: Record, incentives: Incentives): { sum: Decimal; rewardSumByType: Record } => { let sum = new Decimal(0); + const sumPerType: Record = {}; + for (const key of Object.keys(comments)) { const value = comments[key]; + // Initialize the sum for this key if it doesn't exist + if (!sumPerType[key]) { + sumPerType[key] = 0; + } + // if it's a text node calculate word count and multiply with the reward value if (key == "#text") { if (!incentives.comment.totals.word) { continue; } const wordReward = new Decimal(incentives.comment.totals.word); - const reward = wordReward.mul(value.map((str) => str.trim().split(" ").length).reduce((totalWords, wordCount) => totalWords + wordCount, 0)); + const wordCount = value.map((str) => str.trim().split(" ").length).reduce((totalWords, wordCount) => totalWords + wordCount, 0); + const reward = wordReward.mul(wordCount); + sumPerType[key] += wordCount; sum = sum.add(reward); } else { if (!incentives.comment.elements[key]) { @@ -299,9 +309,16 @@ const calculateRewardValue = (comments: Record, incentives: In } const rewardValue = new Decimal(incentives.comment.elements[key]); const reward = rewardValue.mul(value.length); + sumPerType[key] += value.length; sum = sum.add(reward); } } - return sum; + // Convert the summed values back to Decimal + const rewardSumByType: Record = {}; + for (const key of Object.keys(sum)) { + rewardSumByType[key] = new Decimal(sumPerType[key]); + } + + return { sum, rewardSumByType }; }; diff --git a/src/helpers/comment.ts b/src/helpers/comment.ts index 35ef57be0..902d414d9 100644 --- a/src/helpers/comment.ts +++ b/src/helpers/comment.ts @@ -85,3 +85,29 @@ export const createDetailsTable = ( return cleanedHtml; }; + +export const generateCollapsibleTable = (data: { element: string; units: string; reward: string }[]) => { + // Check if the data array is empty + if (data.length === 0) { + return "No data to display."; + } + + // Create the table header row + const headerRow = "| element | units | reward |\n| --- | --- | --- |"; + + // Create the table rows from the data array + const tableRows = data.map((item) => `| ${item.element} | ${item.units} | ${item.reward} |`).join("\n"); + + // Create the complete Markdown table + const tableMarkdown = ` +
+ Click to toggle table + +${headerRow} +${tableRows} + +
+ `; + + return tableMarkdown; +}; From 970f4786387fda1ca76beb032d0aec8e28377673 Mon Sep 17 00:00:00 2001 From: DevPanther Date: Sat, 30 Sep 2023 05:46:08 +0100 Subject: [PATCH 2/2] chore: merge collapsible table in permit table --- src/handlers/comment/handlers/index.ts | 2 +- src/handlers/payout/action.ts | 7 ++- src/handlers/payout/post.ts | 58 +++++++++++------- src/helpers/comment.ts | 81 +++++++++++++++++--------- 4 files changed, 99 insertions(+), 49 deletions(-) diff --git a/src/handlers/comment/handlers/index.ts b/src/handlers/comment/handlers/index.ts index 7e2cb6716..4dcbf7f21 100644 --- a/src/handlers/comment/handlers/index.ts +++ b/src/handlers/comment/handlers/index.ts @@ -60,7 +60,7 @@ export interface RewardsResponse { penaltyAmount: BigNumber; user: string; userId: number; - debug: Record; + debug: Record; }[]; fallbackReward?: Record; } diff --git a/src/handlers/payout/action.ts b/src/handlers/payout/action.ts index e095da752..25bf01562 100644 --- a/src/handlers/payout/action.ts +++ b/src/handlers/payout/action.ts @@ -56,6 +56,7 @@ export interface RewardByUser { type: (string | undefined)[]; user: string | undefined; priceArray: string[]; + debug: Record; } /** @@ -370,6 +371,7 @@ export const handleIssueClosed = async ( type: [conversationRewards.title], user: permit.user, priceArray: [permit.priceInEth.toString()], + debug: permit.debug, }); } }); @@ -388,6 +390,7 @@ export const handleIssueClosed = async ( type: [pullRequestReviewersReward.title], user: permit.user, priceArray: [permit.priceInEth.toString()], + debug: permit.debug, }); } }); @@ -404,6 +407,7 @@ export const handleIssueClosed = async ( type: [creatorReward.title], user: creatorReward.username, priceArray: [creatorReward.reward[0].priceInEth.toString()], + debug: creatorReward.reward[0].debug, }); } else if (creatorReward && creatorReward.reward && creatorReward.reward[0].account === "0x") { logger.info(`Skipping to generate a permit url for missing account. fallback: ${creatorReward.fallbackReward}`); @@ -425,6 +429,7 @@ export const handleIssueClosed = async ( type: title, user: assigneeReward.username, priceArray: [assigneeReward.reward[0].priceInEth.toString()], + debug: assigneeReward.reward[0].debug, }); if (permitComments.length > 0) { @@ -498,7 +503,7 @@ export const handleIssueClosed = async ( const price = `${reward.priceInEth} ${incentivesCalculation.tokenSymbol.toUpperCase()}`; - const comment = createDetailsTable(price, payoutUrl, reward.user, detailsValue); + const comment = createDetailsTable(price, payoutUrl, reward.user, detailsValue, reward.debug); await savePermitToDB(Number(reward.userId), txData); permitComment += comment; diff --git a/src/handlers/payout/post.ts b/src/handlers/payout/post.ts index bb2e715c5..e1ef60e76 100644 --- a/src/handlers/payout/post.ts +++ b/src/handlers/payout/post.ts @@ -67,7 +67,14 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince const fallbackReward: Record = {}; // array of awaiting permits to generate - const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber; debug: Record }[] = []; + const reward: { + account: string; + priceInEth: Decimal; + userId: number; + user: string; + penaltyAmount: BigNumber; + debug: Record; + }[] = []; for (const user of Object.keys(issueCommentsByUser)) { const commentsByUser = issueCommentsByUser[user]; @@ -77,7 +84,7 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince logger.info(`Skipping to generate a permit url because the reward value is 0. user: ${user}`); continue; } - logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); + logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue.sum}`); const account = await getWalletAddress(user); const priceInEth = rewardValue.sum.mul(calculateIncentives.baseMultiplier); if (priceInEth.gt(calculateIncentives.paymentPermitMaxPrice)) { @@ -85,7 +92,7 @@ export const calculateIssueConversationReward = async (calculateIncentives: Ince continue; } if (account) { - reward.push({ account, priceInEth, userId: commentsByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.rewardSumByType }); + reward.push({ account, priceInEth, userId: commentsByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.sumByType }); } else { fallbackReward[user] = priceInEth; } @@ -213,7 +220,14 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I logger.info(`calculatePullRequestReviewsReward: Filtering by the user type done. commentsByUser: ${JSON.stringify(prReviewsByUser)}`); // array of awaiting permits to generate - const reward: { account: string; priceInEth: Decimal; userId: number; user: string; penaltyAmount: BigNumber; debug: Record }[] = []; + const reward: { + account: string; + priceInEth: Decimal; + userId: number; + user: string; + penaltyAmount: BigNumber; + debug: Record; + }[] = []; // The mapping between gh handle and amount in ETH const fallbackReward: Record = {}; @@ -226,7 +240,9 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I logger.info(`calculatePullRequestReviewsReward: Skipping to generate a permit url because the reward value is 0. user: ${user}`); continue; } - logger.info(`calculatePullRequestReviewsReward: Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); + logger.info( + `calculatePullRequestReviewsReward: Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue.sum}` + ); const account = await getWalletAddress(user); const priceInEth = rewardValue.sum.mul(incentivesCalculation.baseMultiplier); if (priceInEth.gt(incentivesCalculation.paymentPermitMaxPrice)) { @@ -235,7 +251,7 @@ export const calculatePullRequestReviewsReward = async (incentivesCalculation: I } if (account) { - reward.push({ account, priceInEth, userId: commentByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.rewardSumByType }); + reward.push({ account, priceInEth, userId: commentByUser.id, user, penaltyAmount: BigNumber.from(0), debug: rewardValue.sumByType }); } else { fallbackReward[user] = priceInEth; } @@ -261,7 +277,7 @@ const generatePermitForComments = async ( logger.info(`No reward for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); return; } - logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue}`); + logger.debug(`Comment parsed for the user: ${user}. comments: ${JSON.stringify(commentsByNode)}, sum: ${rewardValue.sum}`); const account = await getWalletAddress(user); const amountInETH = rewardValue.sum.mul(multiplier); if (amountInETH.gt(paymentPermitMaxPrice)) { @@ -281,16 +297,22 @@ const generatePermitForComments = async ( * @param incentives - The basic price table for reward calculation * @returns - The reward value */ -const calculateRewardValue = (comments: Record, incentives: Incentives): { sum: Decimal; rewardSumByType: Record } => { +const calculateRewardValue = ( + comments: Record, + incentives: Incentives +): { sum: Decimal; sumByType: Record } => { let sum = new Decimal(0); - const sumPerType: Record = {}; + const sumByType: Record = {}; for (const key of Object.keys(comments)) { const value = comments[key]; // Initialize the sum for this key if it doesn't exist - if (!sumPerType[key]) { - sumPerType[key] = 0; + if (!sumByType[key]) { + sumByType[key] = { + count: 0, + reward: new Decimal(0), + }; } // if it's a text node calculate word count and multiply with the reward value @@ -301,7 +323,8 @@ const calculateRewardValue = (comments: Record, incentives: In const wordReward = new Decimal(incentives.comment.totals.word); const wordCount = value.map((str) => str.trim().split(" ").length).reduce((totalWords, wordCount) => totalWords + wordCount, 0); const reward = wordReward.mul(wordCount); - sumPerType[key] += wordCount; + sumByType[key].count += wordCount; + sumByType[key].reward = wordReward; sum = sum.add(reward); } else { if (!incentives.comment.elements[key]) { @@ -309,16 +332,11 @@ const calculateRewardValue = (comments: Record, incentives: In } const rewardValue = new Decimal(incentives.comment.elements[key]); const reward = rewardValue.mul(value.length); - sumPerType[key] += value.length; + sumByType[key].count += value.length; + sumByType[key].reward = rewardValue; sum = sum.add(reward); } } - // Convert the summed values back to Decimal - const rewardSumByType: Record = {}; - for (const key of Object.keys(sum)) { - rewardSumByType[key] = new Decimal(sumPerType[key]); - } - - return { sum, rewardSumByType }; + return { sum, sumByType }; }; diff --git a/src/helpers/comment.ts b/src/helpers/comment.ts index 902d414d9..7be39376c 100644 --- a/src/helpers/comment.ts +++ b/src/helpers/comment.ts @@ -1,3 +1,5 @@ +import Decimal from "decimal.js"; +import { isEmpty } from "lodash"; import * as parse5 from "parse5"; type Node = { @@ -41,12 +43,46 @@ export const parseComments = (comments: string[], itemsToExclude: string[]): Rec return result; }; +export const generateCollapsibleTable = (data: { element: string; units: number; reward: Decimal }[]) => { + // Check if the data array is empty + if (data.length === 0) { + return "No data to display."; + } + + // Create the table header row + const headerRow = "| element | units | reward |\n| --- | --- | --- |"; + + // Create the table rows from the data array + const tableRows = data.map((item) => `| ${item.element} | ${item.units} | ${item.reward} |`).join("\n"); + + // Create the complete Markdown table + const tableMarkdown = ` +
+ Details + +${headerRow} +${tableRows} + +
+ `; + + return tableMarkdown; +}; + export const createDetailsTable = ( amount: string, paymentURL: string, username: string, - values: { title: string; subtitle: string; value: string }[] + values: { title: string; subtitle: string; value: string }[], + debug: Record< + string, + { + count: number; + reward: Decimal; + } + > ): string => { + let collapsibleTable = null; // Generate the table rows based on the values array const tableRows = values .map(({ title, value, subtitle }) => { @@ -61,6 +97,19 @@ export const createDetailsTable = ( }) .join(""); + if (!isEmpty(debug)) { + const data = Object.entries(debug) + .filter(([_, value]) => value.count > 0) + .map(([key, value]) => { + const element = key === "#text" ? "words" : key; + const units = value.count; + const reward = value.reward; + return { element, units, reward }; + }); + + collapsibleTable = generateCollapsibleTable(data); + } + // Construct the complete HTML structure const html = `
@@ -77,37 +126,15 @@ export const createDetailsTable = ( ${tableRows} + ${collapsibleTable ? "COLLAPSIBLE_TABLE_PLACEHOLDER" : ""}
`; // Remove spaces and line breaks from the HTML, ignoring the attributes like and [ ... ] const cleanedHtml = html.replace(/>\s+<").replace(/[\r\n]+/g, ""); - return cleanedHtml; -}; - -export const generateCollapsibleTable = (data: { element: string; units: string; reward: string }[]) => { - // Check if the data array is empty - if (data.length === 0) { - return "No data to display."; - } - - // Create the table header row - const headerRow = "| element | units | reward |\n| --- | --- | --- |"; - - // Create the table rows from the data array - const tableRows = data.map((item) => `| ${item.element} | ${item.units} | ${item.reward} |`).join("\n"); - - // Create the complete Markdown table - const tableMarkdown = ` -
- Click to toggle table - -${headerRow} -${tableRows} - -
- `; + // Add collapsible table here to avoid compression + const finalHtml = cleanedHtml.replace("COLLAPSIBLE_TABLE_PLACEHOLDER", collapsibleTable || ""); - return tableMarkdown; + return finalHtml; };