From b3f20fda0b569057e33aacdfede3ca805d8de5f6 Mon Sep 17 00:00:00 2001 From: Nanak Date: Tue, 23 May 2023 23:50:34 +0530 Subject: [PATCH 1/9] add date-fns library for proper date calculations --- npm-shrinkwrap.json | 20 +++++++++++++++++--- package.json | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 75eab4ab..69b03bd9 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -10,6 +10,7 @@ "dependencies": { "@types/chrome": "^0.0.231", "@types/node-emoji": "^1.8.2", + "date-fns": "^2.30.0", "gpt-tokenizer": "^1.0.5", "node-emoji": "^1.11.0", "react": "^18.0.0", @@ -424,7 +425,6 @@ "version": "7.21.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -1774,6 +1774,21 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4495,8 +4510,7 @@ "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.0", diff --git a/package.json b/package.json index 24407021..538c65c4 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@types/chrome": "^0.0.231", "@types/node-emoji": "^1.8.2", + "date-fns": "^2.30.0", "gpt-tokenizer": "^1.0.5", "node-emoji": "^1.11.0", "react": "^18.0.0", From 72db720d26c378ff5be56561d866079d6b11ef2a Mon Sep 17 00:00:00 2001 From: Nanak Date: Tue, 23 May 2023 23:50:59 +0530 Subject: [PATCH 2/9] add getRelativeDays as a new date util --- src/utils/dateUtils.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/utils/dateUtils.ts diff --git a/src/utils/dateUtils.ts b/src/utils/dateUtils.ts new file mode 100644 index 00000000..c741ac5b --- /dev/null +++ b/src/utils/dateUtils.ts @@ -0,0 +1,15 @@ +export const getRelativeDays = (days: number) => { + if (days === 0) { + return "-"; + } + + if (days >= 365) { + return `${Math.floor(days / 365)}y`; + } + + if (days > 30 && days < 365) { + return `${Math.floor(days / 30)}mo`; + } + + return `${days}d`; +}; From f07cdbf718d322bb5ab569fd4abacb43b2e587ea Mon Sep 17 00:00:00 2001 From: Nanak Date: Tue, 23 May 2023 23:51:35 +0530 Subject: [PATCH 3/9] fetch PR data and calculate user PR Velocity --- src/utils/fetchOpenSaucedApiData.ts | 37 ++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/utils/fetchOpenSaucedApiData.ts b/src/utils/fetchOpenSaucedApiData.ts index b3cefa75..4109a427 100644 --- a/src/utils/fetchOpenSaucedApiData.ts +++ b/src/utils/fetchOpenSaucedApiData.ts @@ -1,5 +1,11 @@ +import differenceInDays from "date-fns/differenceInDays"; import { cachedFetch } from "./cache"; -import { OPEN_SAUCED_USERS_ENDPOINT, OPEN_SAUCED_SESSION_ENDPOINT, OPEN_SAUCED_REPOS_ENDPOINT, OPEN_SAUCED_USER_INSIGHTS_ENDPOINT } from "../constants"; +import { + OPEN_SAUCED_USERS_ENDPOINT, + OPEN_SAUCED_SESSION_ENDPOINT, + OPEN_SAUCED_REPOS_ENDPOINT, + OPEN_SAUCED_USER_INSIGHTS_ENDPOINT, +} from "../constants"; import { IInsight } from "../ts/InsightDto"; export const isOpenSaucedUser = async (username: string) => { @@ -171,3 +177,32 @@ export const updateInsight = async (userToken: string, repoId: string, checked: return response.status === 200; }; + +export const getContributorPullRequestVelocity = async ( + userName: string, + forceRefresh: boolean = false, +) => + cachedFetch(`${OPEN_SAUCED_USERS_ENDPOINT}/${userName}/prs`, { + expireInSeconds: 2 * 60 * 60, + forceRefresh, + headers: { Accept: "application/json" }, + }) + .then(async resp => { + if (!resp?.ok) { + console.log("error getting user info"); + } + return resp?.json(); + }) + .then(({ data }) => { + const mergedPRs = data.filter(prState => prState.state.toLowerCase() === "merged"); + + const totalDays = mergedPRs.reduce((total, pr) => { + const daysBetween = differenceInDays(new Date(pr.closed_at), new Date(pr.created_at)); + + return (total += daysBetween); + }, 0); + + const averageVelocity: number = mergedPRs.length > 0 ? Math.round(totalDays / mergedPRs.length) : 0; + + return averageVelocity; + }); From 5be6f5ab37cf176fb32a20733f3bc5e68caa70b2 Mon Sep 17 00:00:00 2001 From: Nanak Date: Tue, 23 May 2023 23:51:57 +0530 Subject: [PATCH 4/9] add PR velocity in profile page --- src/pages/profile.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 227c4b35..fe150fcd 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -5,9 +5,10 @@ import { AiOutlineReload } from "react-icons/ai"; import { SiC, SiCplusplus, SiCsharp, SiGoland, SiJavascript, SiPhp, SiPython, SiReact, SiRuby, SiRust, SiTypescript } from "react-icons/si"; import { DiJava } from "react-icons/di"; import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { getUserData, getUserPRData, getUserHighlightsData } from "../utils/fetchOpenSaucedApiData"; +import { getUserData, getUserPRData, getUserHighlightsData, getContributorPullRequestVelocity } from "../utils/fetchOpenSaucedApiData"; import { emojify } from "node-emoji"; import { goBack } from "react-chrome-extension-router"; +import { getRelativeDays } from "../utils/dateUtils"; const interestIcon = { python: , @@ -28,18 +29,20 @@ const interestIcon = { type InterestIconKeys = keyof typeof interestIcon; -export const Profile = ({ username }: {username: string}) => { +export const Profile = ({ username }: { username: string }) => { const [user, setUser] = useState(null); const [userPR, setUserPR] = useState(null); const [userHighlights, setUserHighlights] = useState(null); + const [userPRVelocity, setUserPRVelocity] = useState(0); useEffect(() => { const fetchUserData = async () => { - const [userData, userPRData, userHighlightsData] = await Promise.all([getUserData(username), getUserPRData(username), getUserHighlightsData(username)]); + const [userData, userPRData, userHighlightsData, userPRVelocity] = await Promise.all([getUserData(username), getUserPRData(username), getUserHighlightsData(username), getContributorPullRequestVelocity(username)]); setUser(userData); setUserPR(userPRData); setUserHighlights(userHighlightsData); + setUserPRVelocity(userPRVelocity); }; void fetchUserData(); @@ -163,7 +166,9 @@ export const Profile = ({ username }: {username: string}) => {

Avg PRs Velocity

-

-

+

+ {getRelativeDays(userPRVelocity)} +

From 912cd5511293407cc7ede48de4672bdadd022056 Mon Sep 17 00:00:00 2001 From: Nanak Date: Wed, 24 May 2023 06:59:29 +0530 Subject: [PATCH 5/9] remove getContributorPullRequestVelocity from fetchOpenSaucedApiData.ts --- src/utils/fetchOpenSaucedApiData.ts | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/utils/fetchOpenSaucedApiData.ts b/src/utils/fetchOpenSaucedApiData.ts index 4109a427..f7fdfd45 100644 --- a/src/utils/fetchOpenSaucedApiData.ts +++ b/src/utils/fetchOpenSaucedApiData.ts @@ -177,32 +177,3 @@ export const updateInsight = async (userToken: string, repoId: string, checked: return response.status === 200; }; - -export const getContributorPullRequestVelocity = async ( - userName: string, - forceRefresh: boolean = false, -) => - cachedFetch(`${OPEN_SAUCED_USERS_ENDPOINT}/${userName}/prs`, { - expireInSeconds: 2 * 60 * 60, - forceRefresh, - headers: { Accept: "application/json" }, - }) - .then(async resp => { - if (!resp?.ok) { - console.log("error getting user info"); - } - return resp?.json(); - }) - .then(({ data }) => { - const mergedPRs = data.filter(prState => prState.state.toLowerCase() === "merged"); - - const totalDays = mergedPRs.reduce((total, pr) => { - const daysBetween = differenceInDays(new Date(pr.closed_at), new Date(pr.created_at)); - - return (total += daysBetween); - }, 0); - - const averageVelocity: number = mergedPRs.length > 0 ? Math.round(totalDays / mergedPRs.length) : 0; - - return averageVelocity; - }); From e3803eabaae5017231c26f169d47d7e515b3d9e5 Mon Sep 17 00:00:00 2001 From: Nanak Date: Wed, 24 May 2023 07:12:49 +0530 Subject: [PATCH 6/9] add IUserPR type for PR API responses --- src/ts/types.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/ts/types.ts diff --git a/src/ts/types.ts b/src/ts/types.ts new file mode 100644 index 00000000..bf27518e --- /dev/null +++ b/src/ts/types.ts @@ -0,0 +1,19 @@ +interface IUserPR { + readonly title: string; + readonly author_login: string; + readonly state: string; + readonly created_at: string; + readonly closed_at: string; + readonly merged_at: string; + readonly updated_at: string; + readonly filesCount: number; + linesCount: number; + readonly merged: boolean; + readonly draft: boolean; + readonly full_name: string; + readonly number: number; + readonly additions: number; + readonly deletions: number; + readonly changed_files: number; + readonly repo_id: number; +} From b9f6cad865eac49fe6221e3ed8c6d7b367f71ed5 Mon Sep 17 00:00:00 2001 From: Nanak Date: Wed, 24 May 2023 07:13:20 +0530 Subject: [PATCH 7/9] add getUserPRVelocity utility function --- src/utils/getUserPRVelocity.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/utils/getUserPRVelocity.ts diff --git a/src/utils/getUserPRVelocity.ts b/src/utils/getUserPRVelocity.ts new file mode 100644 index 00000000..b0fcc57e --- /dev/null +++ b/src/utils/getUserPRVelocity.ts @@ -0,0 +1,15 @@ +import { differenceInDays } from "date-fns"; + +export const getUserPRVelocity = ((prDetails: IUserPR[]) => { + const mergedPRs = prDetails.filter((prState: IUserPR) => prState.state.toLowerCase() === "merged"); + + const totalDays = mergedPRs.reduce((total: number, pr: IUserPR) => { + const daysBetween = differenceInDays(new Date(pr.closed_at), new Date(pr.created_at)); + + return (total += daysBetween); + }, 0); + + const averageVelocity: number = mergedPRs.length > 0 ? Math.round(totalDays / mergedPRs.length) : 0; + + return averageVelocity; +}); From ef524f3e14f882fc1e7a4852f243c241b74b1725 Mon Sep 17 00:00:00 2001 From: Nanak Date: Wed, 24 May 2023 07:17:18 +0530 Subject: [PATCH 8/9] use getUserPRVelocity function in the profile page --- src/pages/profile.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index fe150fcd..6b864a47 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -5,10 +5,11 @@ import { AiOutlineReload } from "react-icons/ai"; import { SiC, SiCplusplus, SiCsharp, SiGoland, SiJavascript, SiPhp, SiPython, SiReact, SiRuby, SiRust, SiTypescript } from "react-icons/si"; import { DiJava } from "react-icons/di"; import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { getUserData, getUserPRData, getUserHighlightsData, getContributorPullRequestVelocity } from "../utils/fetchOpenSaucedApiData"; +import { getUserData, getUserPRData, getUserHighlightsData } from "../utils/fetchOpenSaucedApiData"; import { emojify } from "node-emoji"; import { goBack } from "react-chrome-extension-router"; import { getRelativeDays } from "../utils/dateUtils"; +import { getUserPRVelocity } from "../utils/getUserPRVelocity"; const interestIcon = { python: , @@ -37,12 +38,12 @@ export const Profile = ({ username }: { username: string }) => { useEffect(() => { const fetchUserData = async () => { - const [userData, userPRData, userHighlightsData, userPRVelocity] = await Promise.all([getUserData(username), getUserPRData(username), getUserHighlightsData(username), getContributorPullRequestVelocity(username)]); + const [userData, userPRData, userHighlightsData] = await Promise.all([getUserData(username), getUserPRData(username), getUserHighlightsData(username)]); setUser(userData); setUserPR(userPRData); setUserHighlights(userHighlightsData); - setUserPRVelocity(userPRVelocity); + setUserPRVelocity(getUserPRVelocity(userPRData?.data || [])); }; void fetchUserData(); From 7db8d5301f1d9974ff47e6d6f27bca8fe5edadb0 Mon Sep 17 00:00:00 2001 From: Nanak Date: Wed, 24 May 2023 08:39:24 +0530 Subject: [PATCH 9/9] export IUserPR from types --- src/ts/types.ts | 2 +- src/utils/getUserPRVelocity.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ts/types.ts b/src/ts/types.ts index bf27518e..4b4fb877 100644 --- a/src/ts/types.ts +++ b/src/ts/types.ts @@ -1,4 +1,4 @@ -interface IUserPR { +export interface IUserPR { readonly title: string; readonly author_login: string; readonly state: string; diff --git a/src/utils/getUserPRVelocity.ts b/src/utils/getUserPRVelocity.ts index b0fcc57e..59f73828 100644 --- a/src/utils/getUserPRVelocity.ts +++ b/src/utils/getUserPRVelocity.ts @@ -1,4 +1,5 @@ import { differenceInDays } from "date-fns"; +import { IUserPR } from "../ts/types"; export const getUserPRVelocity = ((prDetails: IUserPR[]) => { const mergedPRs = prDetails.filter((prState: IUserPR) => prState.state.toLowerCase() === "merged");