This commit is contained in:
hannathkadher
2025-04-15 20:01:42 +04:00
parent 9f3dce535d
commit 5d379e0c36
5 changed files with 134 additions and 2 deletions

View File

@ -4,13 +4,20 @@ import {
Controller,
Delete,
Get,
Header,
Param,
Post,
Put,
Query,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import {
ApiBearerAuth,
ApiOperation,
ApiProduces,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { ProjectService } from '../services';
import { CreateProjectDto, GetProjectParam } from '../dto';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
@ -86,4 +93,24 @@ export class ProjectController {
async findOne(@Param() params: GetProjectParam): Promise<BaseResponseDto> {
return this.projectService.getProject(params.projectUuid);
}
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_SUMMARY,
description:
ControllerRoute.PROJECT.ACTIONS.EXPORT_STRUCTURE_CSV_DESCRIPTION,
})
@Get(':projectUuid/structure/export-csv')
@ApiProduces('text/csv')
@ApiResponse({
status: 200,
description:
'A CSV file containing project details and their structural hierarchy (Project, Community, Space, Parent Space).',
})
@Header('Content-Type', 'text/csv')
@Header('Content-Disposition', 'attachment; filename=project-structure.csv')
async exportStructures(@Param() params: GetProjectParam) {
return this.projectService.exportToCsv(params);
}
}

View File

@ -1,6 +1,6 @@
import { ProjectRepository } from '@app/common/modules/project/repositiories';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { CreateProjectDto } from '../dto';
import { CreateProjectDto, GetProjectParam } from '../dto';
import { BaseResponseDto } from '@app/common/dto/base.response.dto';
import { SuccessResponseDto } from '@app/common/dto/success.response.dto';
import { ProjectEntity } from '@app/common/modules/project/entities';
@ -13,6 +13,8 @@ import { PageResponse } from '@app/common/dto/pagination.response.dto';
import { CommandBus } from '@nestjs/cqrs';
import { CreateOrphanSpaceCommand } from '../command/create-orphan-space-command';
import { UserRepository } from '@app/common/modules/user/repositories';
import { format } from '@fast-csv/format';
import { PassThrough } from 'stream';
@Injectable()
export class ProjectService {
@ -213,4 +215,62 @@ export class ProjectService {
async validate(name: string): Promise<boolean> {
return await this.projectRepository.exists({ where: { name } });
}
async exportToCsv(param: GetProjectParam): Promise<PassThrough> {
try {
const project = await this.projectRepository.findOne({
where: { uuid: param.projectUuid },
relations: [
'communities',
'communities.spaces',
'communities.spaces.parent',
'communities.spaces.tags',
],
});
if (!project) {
throw new HttpException(
`Project with UUID ${param.projectUuid} not found`,
HttpStatus.NOT_FOUND,
);
}
const stream = new PassThrough();
const csvStream = format({ headers: true });
csvStream.pipe(stream);
for (const community of project.communities || []) {
for (const space of community.spaces || []) {
const tagNames = space.tags?.map((tag) => tag.tag).join(', ') || '';
csvStream.write({
'Project Name': project.name,
'Project UUID': project.uuid,
'Community Name': community.name,
'Community UUID': community.uuid,
'Space Name': space.spaceName,
'Space UUID': space.uuid,
'Parent Space UUID': space.parent?.uuid || '',
'Space Tuya UUID': space.spaceTuyaUuid || '',
X: space.x,
Y: space.y,
Tags: tagNames,
});
}
}
csvStream.end();
return stream;
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
`Failed to export project structure for UUID ${param.projectUuid}: ${error.message}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}