diff --git a/src/app.module.ts b/src/app.module.ts
index 5f24362..6f3a43d 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -35,6 +35,7 @@ import { DailyEccmReportTask } from './reports/daily-eccm.report.task';
import { DailyEccmReportsUserCommentsDatasource } from './couchdb-datasources/daily-eccm-reports-user-comments.datasource';
import { DailyEccmUserCommentsService } from './reports/daily-eccm-user-comments.service';
import { SetDailyEccmUserCommentBotHandlerService } from './telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service';
+import { DailyEccmWithExtraDataService } from './reports/daily-eccm-with-extra-data.service';
@Module({
imports: [
@@ -76,6 +77,7 @@ import { SetDailyEccmUserCommentBotHandlerService } from './telegram-bot/handler
DailyEccmReportsUserCommentsDatasource,
DailyEccmUserCommentsService,
SetDailyEccmUserCommentBotHandlerService,
+ DailyEccmWithExtraDataService,
],
})
export class AppModule implements OnModuleInit {
@@ -93,6 +95,8 @@ export class AppModule implements OnModuleInit {
private telegramBotService: TelegramBotService,
private personalNotificationAdapterService: PersonalNotificationAdapterService,
private statusChangeAdapterService: StatusChangeAdapterService,
+ private dailyEccmUserCommentsService: DailyEccmUserCommentsService,
+ private setDailyEccmUserCommentBotHandlerService: SetDailyEccmUserCommentBotHandlerService,
) {}
onModuleInit() {
@@ -165,5 +169,19 @@ export class AppModule implements OnModuleInit {
`Save result process success finished, issue_id = ${args.saveResult.current.id}`,
);
});
+
+ this.initDailyEccmUserCommentsPipeline();
+ }
+
+ private initDailyEccmUserCommentsPipeline(): void {
+ this.setDailyEccmUserCommentBotHandlerService.$messages.subscribe(
+ (data) => {
+ this.dailyEccmUserCommentsService.setComment(
+ data.userId,
+ data.date,
+ data.comment,
+ );
+ },
+ );
}
}
diff --git a/src/converters/redmine-public-url.converter.ts b/src/converters/redmine-public-url.converter.ts
index da7b4d2..245d979 100644
--- a/src/converters/redmine-public-url.converter.ts
+++ b/src/converters/redmine-public-url.converter.ts
@@ -2,6 +2,8 @@ import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
+// TODO: Этот сервис возможно перенести в lib event-emitter
+
@Injectable()
export class RedminePublicUrlConverter {
private redminePublicUrlPrefix: string;
@@ -24,4 +26,48 @@ export class RedminePublicUrlConverter {
const url = this.getUrl(issue.id);
return `${issue.tracker.name} #${issue.id}`;
}
+
+ getMinHtmlHref(issueId: number | string): string {
+ const url = this.getUrl(issueId);
+ return `#${issueId}`;
+ }
+
+ /**
+ * Обогащение текста с идентификаторами задач html ссылками на эти задачи
+ *
+ * Например текст `"Эта ошибка будет решена в рамках задач #123 и #456"`
+ * будет заменён на `"Эта ошибка будет решена в рамках задач
+ * #123 и
+ * #456"`
+ *
+ * @param text
+ * @param linkGenerator функция замены отдельного идентификатора на html ссылку. По умолчанию
+ * будет использоваться собственная функция this.getMinHtmlHref
+ * @see convert
+ */
+ enrichTextWithIssues(
+ text: string,
+ linkGenerator?: (issueId: number | string) => string,
+ ): string {
+ const generator = linkGenerator
+ ? linkGenerator
+ : (issueId) => this.getMinHtmlHref(issueId);
+
+ const regexp = /^\d+/;
+
+ const parts = text.split('#');
+ for (let i = 0; i < parts.length; i++) {
+ let part = parts[i];
+ const match = part.match(regexp);
+ if (!match) {
+ continue;
+ }
+ const issueId = match[0];
+ const replacment = generator(issueId);
+ part = part.replace(new RegExp(`^${issueId}`), replacment);
+ parts[i] = part;
+ }
+
+ return parts.join('');
+ }
}
diff --git a/src/reports/daily-eccm-with-extra-data.service.ts b/src/reports/daily-eccm-with-extra-data.service.ts
new file mode 100644
index 0000000..c798a4c
--- /dev/null
+++ b/src/reports/daily-eccm-with-extra-data.service.ts
@@ -0,0 +1,43 @@
+import { Timestamped } from '@app/event-emitter/models/timestamped';
+import { Injectable } from '@nestjs/common';
+import nano from 'nano';
+import { RedminePublicUrlConverter } from 'src/converters/redmine-public-url.converter';
+import { DailyEccmUserCommentsService } from './daily-eccm-user-comments.service';
+import {
+ DailyEccmReport,
+ DailyEccmReportService,
+} from './daily-eccm.report.service';
+
+@Injectable()
+export class DailyEccmWithExtraDataService {
+ constructor(
+ private dailyEccmReportService: DailyEccmReportService,
+ private dailyEccmUserCommentsService: DailyEccmUserCommentsService,
+ private redminePublicUrlConverter: RedminePublicUrlConverter,
+ ) {}
+
+ async loadReport(
+ name: string,
+ ): Promise<
+ | (DailyEccmReport.Models.Report & nano.DocumentGetResponse & Timestamped)
+ | null
+ > {
+ const baseReportData = await this.dailyEccmReportService.loadReport(name);
+ if (!baseReportData) return null;
+ const userIds = baseReportData.byUsers.map((item) => item.user.id);
+ const userComments = await this.dailyEccmUserCommentsService.loadComments(
+ userIds,
+ name,
+ );
+ for (let i = 0; i < baseReportData.byUsers.length; i++) {
+ const byUser = baseReportData.byUsers[i];
+ if (userComments[byUser.user.id]) {
+ byUser.dailyMessage =
+ this.redminePublicUrlConverter.enrichTextWithIssues(
+ userComments[byUser.user.id],
+ );
+ }
+ }
+ return baseReportData;
+ }
+}
diff --git a/src/reports/daily-eccm.report.controller.ts b/src/reports/daily-eccm.report.controller.ts
index 3c9f971..1c05b14 100644
--- a/src/reports/daily-eccm.report.controller.ts
+++ b/src/reports/daily-eccm.report.controller.ts
@@ -1,4 +1,5 @@
import { Controller, Get, Param, Query, Render } from '@nestjs/common';
+import { DailyEccmWithExtraDataService } from './daily-eccm-with-extra-data.service';
import {
DailyEccmReport,
DailyEccmReportService,
@@ -6,7 +7,10 @@ import {
@Controller('daily-eccm')
export class DailyEccmReportController {
- constructor(private dailyEccmReportService: DailyEccmReportService) {}
+ constructor(
+ private dailyEccmReportService: DailyEccmReportService,
+ private dailyEccmWithExtraDataService: DailyEccmWithExtraDataService,
+ ) {}
@Get()
@Render('daily-eccm-report')
@@ -67,4 +71,19 @@ export class DailyEccmReportController {
return data;
}
}
+
+ @Get('/load/:name/extended/raw')
+ async loadExtendedReportRawData(
+ @Param('name') name: string,
+ ): Promise {
+ return await this.dailyEccmWithExtraDataService.loadReport(name);
+ }
+
+ @Get('/load/:name/extended')
+ @Render('daily-eccm-report-extended')
+ async loadExtendedReport(
+ @Param('name') name: string,
+ ): Promise {
+ return await this.dailyEccmWithExtraDataService.loadReport(name);
+ }
}
diff --git a/src/telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service.ts b/src/telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service.ts
index ba8ab6f..3a188b2 100644
--- a/src/telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service.ts
+++ b/src/telegram-bot/handlers/set-daily-eccm-user-comment.bot-handler.service.ts
@@ -25,7 +25,6 @@ export class SetDailyEccmUserCommentBotHandlerService
private service: TelegramBotService;
private regexp = /\/set_daily_eccm_user_comment (.+)/;
private logger = new Logger(SetDailyEccmUserCommentBotHandlerService.name);
- private dateParamRegexp = /^date=([\d\-]+) (.+)$/;
$messages =
new Subject();
@@ -43,46 +42,58 @@ export class SetDailyEccmUserCommentBotHandlerService
msg.chat.id,
);
const redmineUserId = userMetaInfo.user_id;
- const data = this.parseDate(msg.text);
- this.logger.debug(
- `Setting user comment for daily eccm ` +
- `by redmineUserId = ${redmineUserId}, ` +
- `full text - ${msg.text}, ` +
- `parsed data - ${JSON.stringify(data)}`,
- );
- this.$messages.next({
- userId: redmineUserId,
- date: data.date,
- comment: data.msg,
- });
- return;
+ const data = this.parseData(msg.text);
+ if (data) {
+ this.logger.debug(
+ `Setting user comment for daily eccm ` +
+ `by redmineUserId = ${redmineUserId}, ` +
+ `full text - ${msg.text}, ` +
+ `parsed data - ${JSON.stringify(data)}`,
+ );
+ this.$messages.next({
+ userId: redmineUserId,
+ date: data.date,
+ comment: data.msg,
+ });
+ } else {
+ this.logger.error(
+ `For some reason, it was not possible to get data from an incoming message - ${msg.text}`,
+ );
+ }
});
}
getHelpMsg(): string {
return (
`/set_daily_eccm_user_comment ` +
- `[дата=yyyy-MM-dd] <комментарий> ` +
+ `[date=yyyy-MM-dd] <комментарий> ` +
`- дополнительный комментарий для дейли`
);
}
- private parseDate(src: string): { date: string; msg: string } | null {
- let msgWithoutCommand: any = src.match(this.regexp);
- if (!msgWithoutCommand || !msgWithoutCommand[1]) {
+ private parseData(src: string): { date: string; msg: string } | null {
+ let text = src;
+
+ text = text.replace('/set_daily_eccm_user_comment', '').trim();
+ if (!text) {
return null;
}
- msgWithoutCommand = msgWithoutCommand[1];
- const msgWithDate: any = msgWithoutCommand.match(this.dateParamRegexp);
- let date: any = '';
- if (msgWithDate && msgWithDate[1] && msgWithDate[2]) {
- date = msgWithDate[1];
- if (DateTime.fromFormat(date, ISO_DATE_FORMAT).isValid) {
- msgWithoutCommand = msgWithDate[2];
- } else {
- date = '';
- }
+
+ const dateMatch = text.match(/^date=[\d-]+/);
+ if (!dateMatch) {
+ return { date: DateTime.now().toFormat(ISO_DATE_FORMAT), msg: text };
}
- return { date: date, msg: msgWithoutCommand };
+
+ const datePart = dateMatch[0];
+ let dateRaw = datePart.replace('date=', '');
+ text = text.replace('date=', '').trim();
+ const date = DateTime.fromFormat(dateRaw, ISO_DATE_FORMAT);
+ if (!date.isValid) {
+ this.logger.error(`Wrong date in message - ${src}`);
+ return null;
+ }
+ text = text.replace(dateRaw, '').trim();
+ dateRaw = date.toFormat(ISO_DATE_FORMAT);
+ return { date: dateRaw, msg: text };
}
}
diff --git a/tsconfig.json b/tsconfig.json
index 85879f4..0408fb2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
- "target": "es2017",
+ "target": "es2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
diff --git a/views/daily-eccm-report-extended.hbs b/views/daily-eccm-report-extended.hbs
new file mode 100644
index 0000000..ec346da
--- /dev/null
+++ b/views/daily-eccm-report-extended.hbs
@@ -0,0 +1,57 @@
+
+
+
+
+ Daily Eccm Report
+
+
+
+ Параметры отчёта
+
+ - От - {{this.params.from}}
+ - До - {{this.params.to}}
+ - Имя отчёта - {{this.params.name}}
+ - Имя проекта - {{this.params.project}}
+ - Версии - {{this.params.versions}}
+
+
+ Отчёт по работникам
+ {{#each this.byUsers}}
+
+ {{this.user.firstname}} {{this.user.lastname}}
+
+ {{#if this.dailyMessage}}
+ Комментарий
+ {{{this.dailyMessage}}}
+ {{/if}}
+
+ Текущие задачи
+
+ {{#each this.issuesGroupedByStatus}}
+ -
+ {{this.status.name}}:
+
+ {{#each this.issues}}
+ - {{>redmineIssueAHref issue=this.issue}} (приоритет {{this.issue.priority.name}}; версия {{this.issue.fixed_version.name}}) - {{this.issue.subject}}
+ {{/each}}
+
+
+ {{/each}}
+
+
+ Активности за период
+
+ {{#each this.activities}}
+ -
+ {{>redmineIssueAHref issue=this.issue}} (приоритет {{this.issue.priority.name}}; версия {{this.issue.fixed_version.name}}; статус {{this.issue.status.name}}) - {{this.issue.subject}}
+
+ {{#each this.changes}}
+ - {{this.created_on}}: {{this.change_message}}
+ {{/each}}
+
+
+ {{/each}}
+
+ {{/each}}
+
+
\ No newline at end of file