From c66eb9bad4b478cb3a892c3b3248bf8b15e4ccaf Mon Sep 17 00:00:00 2001 From: Artem Zahorodniuk Date: Wed, 2 Oct 2024 15:28:02 +0300 Subject: [PATCH] feat: script should support more than 1 local git repository gf-589 (#589) --- .../setup-analytics-modal.tsx | 7 +- .../analytics-cli/base-analytics-cli.ts | 8 +- .../analytics-cli/init-analytics-cli.ts | 10 +-- .../modules/analytics/analytics.service.ts | 78 +++++++++++++++---- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/apps/frontend/src/pages/project/libs/components/setup-analytics-modal/setup-analytics-modal.tsx b/apps/frontend/src/pages/project/libs/components/setup-analytics-modal/setup-analytics-modal.tsx index b992b5e45..8eed105da 100644 --- a/apps/frontend/src/pages/project/libs/components/setup-analytics-modal/setup-analytics-modal.tsx +++ b/apps/frontend/src/pages/project/libs/components/setup-analytics-modal/setup-analytics-modal.tsx @@ -57,7 +57,7 @@ const SetupAnalyticsModal = ({ const apiKey = project.apiKey as string; const userId = String(authenticatedUser.id); - return `npx @git-fit/analytics@latest track ${apiKey} ${userId} `; + return `npx @git-fit/analytics@latest track ${apiKey} ${userId} ...`; }, [hasProjectApiKey, hasAuthenticatedUser, project, authenticatedUser]); const { control, errors, handleSubmit, handleValueSet } = useAppForm({ @@ -233,8 +233,9 @@ const SetupAnalyticsModal = ({ Prepare the script.

- Copy the command below and replace <project-path> - placeholder with your local repository's path: + Copy the command below and replace <project-path-1>, + <project-path-2>, ... placeholder with your local + repositories paths:

") + .command("track ") .description("Start the background job for collecting statistics") - .action(async (apiKey: string, userId: string, repoPath: string) => { - if (!apiKey || !userId || !repoPath) { + .action(async (apiKey: string, userId: string, repoPaths: string[]) => { + if (!apiKey || !userId || repoPaths.length === 0) { this.logger.error("Not all command arguments are provided."); return; @@ -80,7 +80,7 @@ class BaseAnalyticsCli { pm2.start( { - args: [apiKey, userId, repoPath], + args: [apiKey, userId, ...repoPaths], autorestart: false, error: `${project.projectName}-err.log`, name: project.projectName, diff --git a/scripts/analytics/src/libs/modules/analytics-cli/init-analytics-cli.ts b/scripts/analytics/src/libs/modules/analytics-cli/init-analytics-cli.ts index a35003963..f685f3452 100644 --- a/scripts/analytics/src/libs/modules/analytics-cli/init-analytics-cli.ts +++ b/scripts/analytics/src/libs/modules/analytics-cli/init-analytics-cli.ts @@ -10,17 +10,15 @@ import { CRON_SCHEDULE, } from "./libs/constants/constants.js"; -const [apiKey, userId, repoPath] = process.argv.slice(ARGUMENT_START_INDEX) as [ - string, - string, - string, -]; +const [apiKey, userId, ...repoPaths] = process.argv.slice( + ARGUMENT_START_INDEX, +) as [string, string, string]; const analyticsService = new AnalyticsService({ analyticsApi, apiKey, gitService, - repoPath, + repoPaths, userId, }); diff --git a/scripts/analytics/src/modules/analytics/analytics.service.ts b/scripts/analytics/src/modules/analytics/analytics.service.ts index ced41fa1b..3ca565758 100644 --- a/scripts/analytics/src/modules/analytics/analytics.service.ts +++ b/scripts/analytics/src/modules/analytics/analytics.service.ts @@ -17,7 +17,7 @@ type Constructor = { analyticsApi: typeof analyticsApi; apiKey: string; gitService: GITService; - repoPath: string; + repoPaths: string[]; userId: string; }; @@ -25,29 +25,29 @@ class AnalyticsService { private analyticsApi: typeof analyticsApi; private apiKey: string; private gitService: GITService; - private repoPath: string; + private repoPaths: string[]; private userId: string; public constructor({ analyticsApi, apiKey, gitService, - repoPath, + repoPaths, userId, }: Constructor) { this.analyticsApi = analyticsApi; this.apiKey = apiKey; this.gitService = gitService; - this.repoPath = repoPath; + this.repoPaths = repoPaths; this.userId = userId; } - private async collectStatsByRepository(): Promise< - ActivityLogCreateItemRequestDto[] - > { + private async collectStatsByRepository( + repoPath: string, + ): Promise { const stats: ActivityLogCreateItemRequestDto[] = []; const shortLogResult = await executeCommand( - this.gitService.getShortLogCommand(this.repoPath, "midnight"), + this.gitService.getShortLogCommand(repoPath, "midnight"), ); const commitItems: CommitStatistics[] = []; @@ -77,15 +77,21 @@ class AnalyticsService { return stats; } - private async fetchRepository(): Promise { - await executeCommand(this.gitService.getFetchCommand(this.repoPath)); - logger.info(`Fetched latest updates for repo at path: ${this.repoPath}`); + private async fetchRepository(repoPath: string): Promise { + await executeCommand(this.gitService.getFetchCommand(repoPath)); + logger.info(`Fetched latest updates for repo at path: ${repoPath}`); } public async collectAndSendStats(): Promise { try { - await this.fetchRepository(); - const stats = await this.collectStatsByRepository(); + const statsAll = []; + + for (const repoPath of this.repoPaths) { + await this.fetchRepository(repoPath); + statsAll.push(...(await this.collectStatsByRepository(repoPath))); + } + + const stats = mergeStats(statsAll); if ( stats[FIRST_ARRAY_INDEX] && @@ -116,4 +122,50 @@ class AnalyticsService { } } +function mergeStats( + statsAll: ActivityLogCreateItemRequestDto[], +): ActivityLogCreateItemRequestDto[] { + return mergeByCriteria( + statsAll, + (item1, item2) => item1.date === item2.date, + (mergedItem, item) => + (mergedItem.items = mergeStatsItems([ + ...mergedItem.items, + ...item.items, + ])), + ); +} + +function mergeStatsItems(items: CommitStatistics[]): CommitStatistics[] { + return mergeByCriteria( + items, + (item1, item2) => + item1.authorEmail === item2.authorEmail && + item1.authorName === item2.authorName, + (mergedItem, item) => (mergedItem.commitsNumber += item.commitsNumber), + ); +} + +function mergeByCriteria( + items: T[], + compareFunction: (item1: T, item2: T) => boolean, + mergeFunction: (mergedItem: T, item: T) => void, +): T[] { + const mergedItems: T[] = []; + + for (const item of items) { + const mergedItem = mergedItems.find((mergedItem) => + compareFunction(mergedItem, item), + ); + + if (mergedItem) { + mergeFunction(mergedItem, item); + } else { + mergedItems.push(item); + } + } + + return mergedItems; +} + export { AnalyticsService };