From f9c36582e18015a47901c96ea183864b8bdc8479 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 4 Jul 2024 18:32:03 +0700 Subject: [PATCH] feat: integration export report --- package.json | 1 + .../report-export/report-export.service.ts | 154 +++++++++++++++++- .../shared/dto/report-export.get.dto.ts | 6 - yarn.lock | 5 + 4 files changed, 154 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 4728bd1..68b66fd 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "elastic-apm-node": "^4.5.4", "exceljs": "^4.4.0", "googleapis": "^140.0.0", + "moment": "^2.30.1", "nano": "^10.1.3", "pg": "^8.11.5", "plop": "^4.0.1", diff --git a/src/modules/reports/report-export/report-export.service.ts b/src/modules/reports/report-export/report-export.service.ts index 492f494..41034ea 100644 --- a/src/modules/reports/report-export/report-export.service.ts +++ b/src/modules/reports/report-export/report-export.service.ts @@ -1,4 +1,10 @@ -import { Injectable, Logger, Scope } from '@nestjs/common'; +import { + Injectable, + Logger, + NotFoundException, + Scope, + UnprocessableEntityException, +} from '@nestjs/common'; import { BaseReportService } from '../shared/services/base-report.service'; import { CreateReportExportDto } from '../shared/dto/report-export.create.dto'; import { @@ -19,6 +25,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ExcelJS from 'exceljs'; import { roundingCurrency } from '../shared/helpers'; +import { createPaginationMeta } from 'src/core/response/domain/utils/pagination-meta.helper'; +import * as moment from 'moment'; @Injectable({ scope: Scope.REQUEST }) export class ReportExportService extends BaseReportService { @@ -97,7 +105,7 @@ export class ReportExportService extends BaseReportService { group_name: config.group_name, unique_name: config.unique_name, label: config.label, - file_name: fileName, + file_name: fName, file_url: null, total_data: totalRow, processing_data: 0, @@ -304,18 +312,152 @@ export class ReportExportService extends BaseReportService { } async getAll(query: GetReportExportDto) { - return 'you hit API for get all report export'; + const modelName = ExportReportHistoryModel.name; + + const page = query.page; + const limit = query.limit; + + const creator_id = this.getUser().id; + const group_names = query.group_names; + const unique_names = query.unique_names; + const statuses = query.statuses; + + const qb = this.exportHistoryRepo + .createQueryBuilder(modelName) + .where((query) => { + if (unique_names) { + query.andWhere(`unique_name IN (:...unique_names)`, { unique_names }); + } + if (group_names) { + query.andWhere(`group_name IN (:...group_names)`, { group_names }); + } + + if (statuses) { + query.andWhere(`status IN (:...statuses)`, { statuses }); + } + + query.andWhere(`creator_id = :creator_id`, { creator_id }); + }) + .orderBy(`${modelName}.created_at`, 'DESC'); + + const [data, total] = await qb + .take(+limit) + .skip(+limit * +page - +limit) + .getManyAndCount(); + + const meta = createPaginationMeta(page, limit, data.length, total); + + return { data, meta }; } async delete(id: string) { - return 'you hit API for delete report export'; + const findData = await this.exportHistoryRepo.findOneBy({ id }); + if (findData && findData?.file_url) { + try { + const directory = './uploads/report-data/'; + const filePath = path.join(directory, findData.file_url); + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + await this.exportHistoryRepo.delete(id); + this.logger.warn( + `Successfully deleted the ${findData.file_url} file`, + ); + return { success: true, message: 'File deleted successfully' }; + } else { + throw new NotFoundException('File not found'); + } + } catch (error) { + throw new UnprocessableEntityException('File could not be deleted'); + } + } + throw new UnprocessableEntityException(); + } + + async updateFailedData(group_names, unique_names) { + const creator_id = this.getUser().id; + const aMinutesAgo = moment().subtract(5, 'minutes').valueOf(); + + await this.exportHistoryRepo + .createQueryBuilder() + .update(ExportReportHistoryModel) + .set({ + status: REPORT_HISTORY_STATUS.FAILED, + updated_at: moment().valueOf(), + }) + .where((query) => { + if (group_names) { + query.andWhere(`group_name IN (:...group_names)`, { group_names }); + } + if (unique_names) { + query.andWhere(`unique_name IN (:...unique_names)`, { unique_names }); + } + query.andWhere(`status = :status`, { + status: REPORT_HISTORY_STATUS.PROCESSING, + }); + query.andWhere(`updated_at < :aMinutesAgo`, { aMinutesAgo }); + query.andWhere(`creator_id = :creator_id`, { creator_id }); + }) + .execute(); } async getAllProcessing(query: GetReportExportProcessingDto) { - return 'you hit API for get all processing report export'; + const creator_id = this.getUser().id; + const modelName = ExportReportHistoryModel.name; + + const group_names = query.group_names; + const unique_names = query.unique_names; + + await this.updateFailedData(group_names, unique_names); + const aMinutesAgo = moment().subtract(100, 'seconds').valueOf(); + + const qb = this.exportHistoryRepo + .createQueryBuilder(modelName) + .where((query) => { + if (group_names) { + query.andWhere(`unique_name IN (:...unique_names)`, { unique_names }); + } + if (unique_names) { + query.andWhere(`group_name IN (:...group_names)`, { group_names }); + } + + query.andWhere(`status IN (:...status)`, { + status: ['processing', 'failed', 'done'], + }); + query.andWhere(`updated_at > :aMinutesAgo`, { aMinutesAgo }); + query.andWhere(`creator_id = :creator_id`, { creator_id }); + }) + .orderBy(`${modelName}.created_at`, 'DESC'); + + const data = await qb.getMany(); + + return { + data: data, + }; } async getListHistoryFileName(query: GetReportExportFileNameDto) { - return 'you hit API for get all file name report export'; + const modelName = ExportReportHistoryModel.name; + + const creator_id = this.getUser().id; + const unique_names = query.unique_names; + const group_names = query.group_names; + + const qb = this.exportHistoryRepo + .createQueryBuilder(modelName) + .select(`${modelName}.file_name`) + .where((query) => { + if (unique_names) { + query.andWhere(`unique_name IN (:...unique_names)`, { unique_names }); + } + if (group_names) { + query.andWhere(`group_name IN (:...group_names)`, { group_names }); + } + + query.andWhere(`creator_id = :creator_id`, { creator_id }); + }) + .distinct(true); + + const newData = await qb.getRawMany(); + return newData.map((el) => el.ExportReportHistoryModel_file_name); } } diff --git a/src/modules/reports/shared/dto/report-export.get.dto.ts b/src/modules/reports/shared/dto/report-export.get.dto.ts index 264582e..718dea4 100644 --- a/src/modules/reports/shared/dto/report-export.get.dto.ts +++ b/src/modules/reports/shared/dto/report-export.get.dto.ts @@ -46,12 +46,6 @@ export class GetReportExportProcessingDto { return Array.isArray(body.value) ? body.value : [body.value]; }) unique_names?: string[]; - - @ApiProperty({ type: ['string'], required: false }) - @Transform((body) => { - return Array.isArray(body.value) ? body.value : [body.value]; - }) - statuses?: string; } export class GetReportExportFileNameDto { diff --git a/yarn.lock b/yarn.lock index 557fec5..30ebde9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5174,6 +5174,11 @@ module-details-from-path@^1.0.3: resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== +moment@^2.30.1: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + monitor-event-loop-delay@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7"