From 5913609f96592c3c167404cf09577652d369a0a9 Mon Sep 17 00:00:00 2001 From: Pavel Gnedov Date: Wed, 26 Nov 2025 18:19:04 +0700 Subject: [PATCH] backup draft --- frontend/package-lock.json | 75 +++++++++++++ frontend/package.json | 1 + src/app.module.ts | 4 + src/eccm-statistic/reviewers.service.ts | 13 +++ .../daily-eccm-v2-report-task-handler.ts | 77 -------------- ...aily-eccm-v2-report-task-runner.service.ts | 4 +- .../daily-eccm-v2-report.controller.ts | 30 ++++++ src/reports/daily-eccm-v2-report.service.ts | 100 +++++++++++++++++- 8 files changed, 223 insertions(+), 81 deletions(-) create mode 100644 src/eccm-statistic/reviewers.service.ts delete mode 100644 src/reports/daily-eccm-v2-report-task-handler.ts create mode 100644 src/reports/daily-eccm-v2-report.controller.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 364dfae..c3f589a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -17,6 +17,7 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "axios": "^1.4.0", + "jsonpath-plus": "^10.3.0", "luxon": "^3.3.0", "mobx": "^6.9.0", "mobx-react-lite": "^3.4.3", @@ -2974,6 +2975,28 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -11430,6 +11453,14 @@ } } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -11483,6 +11514,23 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -19116,6 +19164,18 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "requires": {} + }, + "@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "requires": {} + }, "@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", @@ -25275,6 +25335,11 @@ "xml-name-validator": "^3.0.0" } }, + "jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==" + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -25314,6 +25379,16 @@ "universalify": "^2.0.0" } }, + "jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "requires": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + } + }, "jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 026f9c5..d36c432 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "axios": "^1.4.0", + "jsonpath-plus": "^10.3.0", "luxon": "^3.3.0", "mobx": "^6.9.0", "mobx-react-lite": "^3.4.3", diff --git a/src/app.module.ts b/src/app.module.ts index 08c1510..1599ca3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -58,6 +58,8 @@ import { DailyEccmV2ReportTaskRunnerService } from './reports/daily-eccm-v2-repo import { DashboardsService } from '@app/event-emitter/dashboards/dashboards.service'; import { DailyEccmReportsV2Datasource } from './couchdb-datasources/daily-eccm-reports-v2.datasource'; import { DailyEccmReportsV2DataLoaderService } from './eccm-statistic/dashboards/widget-data-loader/daily-eccm-v2.widget-data-loader.service'; +import { DailyEccmV2ReportController } from './reports/daily-eccm-v2-report.controller'; +import { DailyEccmV2ReportService } from './reports/daily-eccm-v2-report.service'; @Module({ imports: [ @@ -82,6 +84,7 @@ import { DailyEccmReportsV2DataLoaderService } from './eccm-statistic/dashboards SimpleIssuesListController, TagsManagerController, TelegramBotController, + DailyEccmV2ReportController, ], providers: [ AppService, @@ -123,6 +126,7 @@ import { DailyEccmReportsV2DataLoaderService } from './eccm-statistic/dashboards DailyEccmReportsV2Datasource, DailyEccmV2ReportTaskRunnerService, DailyEccmReportsV2DataLoaderService, + DailyEccmV2ReportService, ], }) export class AppModule implements OnModuleInit { diff --git a/src/eccm-statistic/reviewers.service.ts b/src/eccm-statistic/reviewers.service.ts new file mode 100644 index 0000000..ea77015 --- /dev/null +++ b/src/eccm-statistic/reviewers.service.ts @@ -0,0 +1,13 @@ +import { RedmineTypes } from '@app/event-emitter/models/redmine-types'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ReviewersService { + constructor() { + return; + } + + async getTasks(): Promise { + return; + } +} diff --git a/src/reports/daily-eccm-v2-report-task-handler.ts b/src/reports/daily-eccm-v2-report-task-handler.ts deleted file mode 100644 index 9426289..0000000 --- a/src/reports/daily-eccm-v2-report-task-handler.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Logger } from '@nestjs/common'; -import { IssuesService } from '@app/event-emitter/issues/issues.service'; - -export type Params = { - query: any; // TODO: add type - schedule: string; -}; - -export type Report = { - id: string; - name: string; - datetime: number; - datetimeFormatted: string; - jobInfo: JobInfo; - params: Params; // query?, schedule?, etc TODO - data: any; -}; - -export type JobInfo = { - jobId: string; - dashboardId: string; - widgetId: string; -}; - -export class DailyEccmV2ReportTaskHandlerService { - private logger = new Logger(DailyEccmV2ReportTaskHandlerService.name); - - private issuesService: IssuesService | null = null; - - constructor(public params: Params, public jobInfo: JobInfo) { - return; - } - - setIssuesService(issuesService: IssuesService): void { - this.issuesService = issuesService; - } - - getIssuesService(): IssuesService | null { - if (!this.issuesService) { - this.logger.warn( - 'DailyEccmV2ReportTaskHandlerService is not initialized, issuesService is null', - ); - return null; - } - return this.issuesService; - } - - async createReport(): Promise { - try { - const report = await this.prepareReportData(); - await this.saveNewReport(report); - } catch (error) { - this.logger.error(error); - } - } - - async prepareReportData(): Promise { - const issuesService = this.getIssuesService(); - if (!issuesService) { - throw new Error( - 'Cannot create report without issuesService, DailyEccmV2ReportTaskHandlerService is not initialized', - ); - } - const issues = await this.issuesService.mergedTreesAndFind( - this.params.query, - ); - return {} as Report; - } - - async saveNewReport(report: Report): Promise { - return true; - } - - async updatePreviousReport(report: Report): Promise { - return true; - } -} diff --git a/src/reports/daily-eccm-v2-report-task-runner.service.ts b/src/reports/daily-eccm-v2-report-task-runner.service.ts index eaab0ae..f86504a 100644 --- a/src/reports/daily-eccm-v2-report-task-runner.service.ts +++ b/src/reports/daily-eccm-v2-report-task-runner.service.ts @@ -8,11 +8,11 @@ import { DailyEccmReportsV2Datasource } from 'src/couchdb-datasources/daily-eccm import { randomUUID } from 'crypto'; import { RedmineTypes } from '@app/event-emitter/models/redmine-types'; import { DateTime } from 'luxon'; -import { Params } from './daily-eccm-v2-report-task-handler'; +// import { Params } from './daily-eccm-v2-report-task-handler'; export type Job = { id: string; - params: Params; + params: any; }; export type JobHandlerParams = { diff --git a/src/reports/daily-eccm-v2-report.controller.ts b/src/reports/daily-eccm-v2-report.controller.ts new file mode 100644 index 0000000..edb3b2e --- /dev/null +++ b/src/reports/daily-eccm-v2-report.controller.ts @@ -0,0 +1,30 @@ +import { Controller, Get, Param, Query, Logger } from '@nestjs/common'; +import { DailyEccmV2ReportService } from './daily-eccm-v2-report.service'; + +@Controller('/api/daily-eccm-v2-report') +export class DailyEccmV2ReportController { + private logger = new Logger(DailyEccmV2ReportController.name); + + constructor(private dailyEccmV2ReportService: DailyEccmV2ReportService) { + return; + } + + @Get(':dashboardId/:widgetId/list') + async getList(@Param() params: any, @Query() query: any): Promise { + const getListParams: any = { + dashboardId: params.dashboardId, + widgetId: params.widgetId, + }; + if (query.limit) getListParams.limit = parseInt(query.limit); + if (query.after) getListParams.after = query.after; + if (query.before) getListParams.before = query.before; + return this.dailyEccmV2ReportService.getList(getListParams); + } + + @Get(':dashboardId/:widgetId/latest') + async getLatest(@Param() params: any): Promise { + const dashboardId = params.dashboardId; + const widgetId = params.widgetId; + return this.dailyEccmV2ReportService.getLatest(dashboardId, widgetId); + } +} diff --git a/src/reports/daily-eccm-v2-report.service.ts b/src/reports/daily-eccm-v2-report.service.ts index 545e219..2781d28 100644 --- a/src/reports/daily-eccm-v2-report.service.ts +++ b/src/reports/daily-eccm-v2-report.service.ts @@ -1,4 +1,100 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Logger } from '@nestjs/common'; +import { DailyEccmReportsV2Datasource } from 'src/couchdb-datasources/daily-eccm-reports-v2.datasource'; +import { DateTime } from 'luxon'; + +export type ListProps = { + dashboardId: string; + widgetId: string; + limit?: number; + /** Milliseconds since Unix epoch */ + after?: number | string; + /** Milliseconds since Unix epoch */ + before?: number | string; +}; @Injectable() -export class DailyEccmV2ReportService {} +export class DailyEccmV2ReportService { + private logger = new Logger(DailyEccmV2ReportService.name); + + constructor() { + return; + } + + async getList(props: ListProps): Promise { + const ds = await DailyEccmReportsV2Datasource.getDatasource(); + const findQuery: any = { + selector: { + $and: [ + { dashboardId: props.dashboardId }, + { widgetId: props.widgetId }, + ], + }, + fields: [ + 'id', + 'dashboardId', + 'widgetId', + 'datetime', + 'datetimeFormatted', + ], + sort: [ + { + datetime: 'asc', + }, + ], + }; + if (props.after) { + const after = + typeof props.after === 'string' + ? DateTime.fromISO(props.after).toMillis() + : Number(props.after); + findQuery.selector.$and.push({ datetime: { $gte: after } }); + } + if (props.before) { + const before = + typeof props.before === 'string' + ? DateTime.fromISO(props.before).toMillis() + : Number(props.before); + findQuery.selector.$and.push({ datetime: { $lte: before } }); + } + if (props.limit) { + findQuery.limit = props.limit; + } + this.logger.log( + `Get list daily eccm v2 report with query: ${JSON.stringify(findQuery)}`, + ); + + const result = await ds.find(findQuery); + if (result && result.docs) { + return result.docs as any; + } + return []; + } + + async getLatest(dashboardId: string, widgetId: string): Promise { + const ds = await DailyEccmReportsV2Datasource.getDatasource(); + const findQuery: any = { + selector: { + $and: [ + { dashboardId: dashboardId }, + { widgetId: widgetId }, + { latest: true }, + ], + }, + fields: [ + 'id', + 'dashboardId', + 'widgetId', + 'datetime', + 'datetimeFormatted', + 'reportIssues', + 'issuesMetrics', + ], + limit: 1, + }; + const result = await ds.find(findQuery); + if (result && result.docs) { + return result.docs[0] as any; + } + return null; + } +}