diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts index e8f0777..a777273 100644 --- a/src/admin/admin.controller.ts +++ b/src/admin/admin.controller.ts @@ -5,6 +5,7 @@ import { NotFoundException, Param, ParseBoolPipe, + ParseEnumPipe, ParseIntPipe, Post, Put, @@ -20,6 +21,7 @@ import { ApiConsumes, ApiNotFoundResponse, ApiOkResponse, + ApiQuery, ApiTags, } from '@nestjs/swagger'; import { @@ -32,6 +34,7 @@ import { import { FilesInterceptor } from '@nestjs/platform-express'; import { JwtAuthGuard } from '../auth/jwt-auth.guard'; import { Request } from 'express'; +import { ReportCategory } from '../common/dto/report-category'; @Controller('admin') @ApiTags('admin') @@ -44,13 +47,14 @@ export class AdminController { description: 'All reports have been successfully found', type: [FullReportDto], }) + @ApiQuery({ name: 'category', enum: ReportCategory, required: false }) @Get('/reports') async getAllReports( @Query('isDeleted', ParseBoolPipe) isDeleted: boolean, + @Query('category', new ParseEnumPipe(ReportCategory, { optional: true })) + category?: ReportCategory, ): Promise { - return isDeleted - ? await this.adminService.getAllDeletedReports() - : await this.adminService.getAllReports(); + return this.adminService.getAllReports(isDeleted, category); } @ApiOkResponse({ diff --git a/src/admin/admin.service.ts b/src/admin/admin.service.ts index 20d41a7..5a4c262 100644 --- a/src/admin/admin.service.ts +++ b/src/admin/admin.service.ts @@ -14,6 +14,7 @@ import { HistoryEditsDto } from './dto/history-edits.dto'; import { StatusRecordsDto } from '../report/dto'; import { UpdateReportDto } from './dto'; import { Dump } from '../repositories/dumps/schemas'; +import { ReportCategory } from '../common/dto/report-category'; @Injectable() export class AdminService { @@ -22,8 +23,14 @@ export class AdminService { private readonly reportRepository: ReportRepository, ) {} - async getAllReports(): Promise { - const reports = await this.reportRepository.getAllReports(); + async getAllReports( + isDeleted: boolean, + category?: ReportCategory, + ): Promise { + const reports = await this.reportRepository.getAllReports( + isDeleted, + category, + ); return reports.map(AdminService.docToFullReport); } @@ -39,11 +46,6 @@ export class AdminService { return AdminService.docToFullDump(dump); } - async getAllDeletedReports(): Promise { - const reports = await this.reportRepository.getDeletedReports(); - return reports.map(AdminService.docToFullReport); - } - async updateReport( updateReport: UpdateReportDto, images: Array, @@ -78,7 +80,6 @@ export class AdminService { return new FullDumpDto( dump._id.toString(), dump.name, - dump.type, dump.reportLong, dump.reportLat, dump.isVisible, @@ -93,7 +94,7 @@ export class AdminService { return new FullReportDto( report.refId, report.name, - report.type, + AdminService.parseReportCategory(report.type), report.refId, report.reportLong, report.reportLat, @@ -127,4 +128,12 @@ export class AdminService { private static docToStatusRecords(records: StatusRecords): StatusRecordsDto { return new StatusRecordsDto(records.status, new Date(records.date)); } + + private static parseReportCategory(value: string): ReportCategory { + if (Object.values(ReportCategory).includes(value as ReportCategory)) { + return value as ReportCategory; + } else { + throw new Error(`Invalid report category: ${value}`); + } + } } diff --git a/src/admin/dto/full-dump.dto.ts b/src/admin/dto/full-dump.dto.ts index 8cb712d..b3d5ad7 100644 --- a/src/admin/dto/full-dump.dto.ts +++ b/src/admin/dto/full-dump.dto.ts @@ -8,9 +8,6 @@ export class FullDumpDto { @ApiProperty() name: string; - @ApiProperty() - type: string; - @ApiProperty({ format: 'double' }) longitude: number; @@ -36,7 +33,6 @@ export class FullDumpDto { constructor( refId: string, name: string, - type: string, longitude: number, latitude: number, isVisible: boolean, @@ -47,7 +43,6 @@ export class FullDumpDto { ) { this.refId = refId; this.name = name; - this.type = type; this.longitude = longitude; this.latitude = latitude; this.isVisible = isVisible; diff --git a/src/admin/dto/full-report.dto.ts b/src/admin/dto/full-report.dto.ts index 8938d2c..320240a 100644 --- a/src/admin/dto/full-report.dto.ts +++ b/src/admin/dto/full-report.dto.ts @@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { HistoryDataDto } from './history-data.dto'; import { StatusRecordsDto } from './status-records.dto'; import { ToBoolean } from '../../common/transform/boolean.transform'; +import { ReportCategory } from '../../common/dto/report-category'; export class FullReportDto { @ApiProperty() @@ -11,7 +12,7 @@ export class FullReportDto { name: string; @ApiProperty() - type: string; + category: ReportCategory; @ApiProperty() refId: string; @@ -57,7 +58,7 @@ export class FullReportDto { constructor( _id: string, name: string, - type: string, + category: ReportCategory, refId: string, longitude: number, latitude: number, @@ -74,7 +75,7 @@ export class FullReportDto { ) { this._id = _id; this.name = name; - this.type = type; + this.category = category; this.refId = refId; this.longitude = longitude; this.latitude = latitude; diff --git a/src/common/dto/report-category.ts b/src/common/dto/report-category.ts new file mode 100644 index 0000000..a4084ea --- /dev/null +++ b/src/common/dto/report-category.ts @@ -0,0 +1,4 @@ +export enum ReportCategory { + trash = 'trash', + forest = 'forest', +} diff --git a/src/dumps/dto/dump.dto.ts b/src/dumps/dto/dump.dto.ts index a562f6a..9a8a39e 100644 --- a/src/dumps/dto/dump.dto.ts +++ b/src/dumps/dto/dump.dto.ts @@ -4,9 +4,6 @@ export class DumpDto { @ApiProperty() name: string; - @ApiProperty() - type: string; - @ApiProperty({ format: 'double' }) reportLong: number; @@ -27,7 +24,6 @@ export class DumpDto { constructor( name: string, - type: string, reportLong: number, reportLat: number, address: string, @@ -36,7 +32,6 @@ export class DumpDto { moreInformation: string, ) { this.name = name; - this.type = type; this.reportLong = reportLong; this.reportLat = reportLat; this.address = address; diff --git a/src/dumps/dump.service.ts b/src/dumps/dump.service.ts index 23ea7d3..e5524dd 100644 --- a/src/dumps/dump.service.ts +++ b/src/dumps/dump.service.ts @@ -11,10 +11,10 @@ export class DumpService { const dumps = await this.dumpRepository.getVisibleDumps(); return dumps.map(this.docToPublicDump); } + private docToPublicDump(e: Dump): DumpDto { return new DumpDto( e.name, - e.type, e.reportLong, e.reportLat, e.address, diff --git a/src/report/dto/create-report.dto.ts b/src/report/dto/create-report.dto.ts index 9f91684..c77fd1e 100644 --- a/src/report/dto/create-report.dto.ts +++ b/src/report/dto/create-report.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsEmail, IsLatitude, IsLongitude, IsNotEmpty } from 'class-validator'; +import { IsEmail, IsEnum, IsLatitude, IsLongitude, IsNotEmpty } from 'class-validator'; +import { ReportCategory } from '../../common/dto/report-category'; export class CreateReportDto { @IsNotEmpty() @@ -11,6 +12,9 @@ export class CreateReportDto { @IsLatitude() latitude: number; + @IsEnum(ReportCategory) + category: ReportCategory; + @IsEmail() email: string; diff --git a/src/report/dto/public-report.dto.ts b/src/report/dto/public-report.dto.ts index 1065f4b..d1dc5e6 100644 --- a/src/report/dto/public-report.dto.ts +++ b/src/report/dto/public-report.dto.ts @@ -1,12 +1,13 @@ import { ApiProperty } from '@nestjs/swagger'; import { StatusRecordsDto } from './status-records.dto'; +import { ReportCategory } from '../../common/dto/report-category'; export class PublicReportDto { @ApiProperty() name: string; @ApiProperty() - type: string; + category: ReportCategory; @ApiProperty() refId: string; @@ -37,7 +38,7 @@ export class PublicReportDto { constructor( name: string, - type: string, + category: ReportCategory, refId: string, longitude: number, latitude: number, @@ -49,7 +50,7 @@ export class PublicReportDto { statusRecords: StatusRecordsDto[], ) { this.name = name; - this.type = type; + this.category = category; this.refId = refId; this.longitude = longitude; this.latitude = latitude; diff --git a/src/report/dto/report-statistics.dto.ts b/src/report/dto/report-statistics.dto.ts index ea7a864..ede17c4 100644 --- a/src/report/dto/report-statistics.dto.ts +++ b/src/report/dto/report-statistics.dto.ts @@ -8,21 +8,17 @@ export class ReportStatisticsDto { @ApiProperty({ type: 'integer' }) totalInvestigatedReports: number; @ApiProperty({ type: 'integer' }) - totalCleanedReports: number; - @ApiProperty({ type: 'integer' }) totalFalseReports: number; constructor( totalSentReports: number, totalInInvestigationReports: number, totalInvestigatedReports: number, - totalCleanedReports: number, totalFalseReports: number, ) { this.totalSentReports = totalSentReports; this.totalInInvestigationReports = totalInInvestigationReports; this.totalInvestigatedReports = totalInvestigatedReports; - this.totalCleanedReports = totalCleanedReports; this.totalFalseReports = totalFalseReports; } } diff --git a/src/report/report.controller.ts b/src/report/report.controller.ts index 9d20c2d..aa94cca 100644 --- a/src/report/report.controller.ts +++ b/src/report/report.controller.ts @@ -4,8 +4,10 @@ import { Get, NotFoundException, Param, + ParseEnumPipe, ParseIntPipe, Post, + Query, UploadedFiles, UseInterceptors, } from '@nestjs/common'; @@ -15,12 +17,14 @@ import { ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, + ApiQuery, ApiTags, } from '@nestjs/swagger'; import { PublicReportDto } from './dto'; import { CreateReportDto } from './dto'; import { FilesInterceptor } from '@nestjs/platform-express'; import { ReportStatisticsDto } from './dto/report-statistics.dto'; +import { ReportCategory } from '../common/dto/report-category'; @Controller('reports') @ApiTags('reports') @@ -45,18 +49,26 @@ export class ReportController { description: 'All visible reports have been successfully found', type: [PublicReportDto], }) + @ApiQuery({ name: 'category', enum: ReportCategory, required: false }) @Get() - getAllPublicReports(): Promise { - return this.reportService.getAllVisibleReports(); + getAllPublicReports( + @Query('category', new ParseEnumPipe(ReportCategory, { optional: true })) + category?: ReportCategory, + ): Promise { + return this.reportService.getAllVisibleReports(category); } @ApiOkResponse({ description: 'Report statistics have been successfully fetched', type: ReportStatisticsDto, }) + @ApiQuery({ name: 'category', enum: ReportCategory, required: false }) @Get('/statistics') - getReportStatistics(): Promise { - return this.reportService.getReportStatistics(); + getReportStatistics( + @Query('category', new ParseEnumPipe(ReportCategory, { optional: true })) + category?: ReportCategory, + ): Promise { + return this.reportService.getReportStatistics(category); } @Get('/:refId') diff --git a/src/report/report.service.ts b/src/report/report.service.ts index 77ab2a7..8e932dd 100644 --- a/src/report/report.service.ts +++ b/src/report/report.service.ts @@ -3,13 +3,16 @@ import { CreateReportDto, PublicReportDto, StatusRecordsDto } from './dto'; import { ReportRepository } from '../repositories/reports/report.repository'; import { Report, StatusRecords } from '../repositories/reports/schemas'; import { ReportStatisticsDto } from './dto/report-statistics.dto'; +import { ReportCategory } from '../common/dto/report-category'; @Injectable() export class ReportService { constructor(private readonly reportRepository: ReportRepository) {} - async getAllVisibleReports(): Promise { - const reports = await this.reportRepository.getVisibleReports(); + async getAllVisibleReports( + category?: ReportCategory, + ): Promise { + const reports = await this.reportRepository.getVisibleReports(category); return reports.map(ReportService.docToPublicReport); } @@ -30,27 +33,32 @@ export class ReportService { return ReportService.docToPublicReport(report); } - async getReportStatistics(): Promise { - const stats = await this.reportRepository.getVisibleStatusCounts(); + async getReportStatistics( + category?: ReportCategory, + ): Promise { + const stats = await this.reportRepository.getVisibleStatusCounts(category); return ReportService.docToReportStatistics(stats); } private static docToReportStatistics(e: any): ReportStatisticsDto { return new ReportStatisticsDto( - e.filter((stat: { _id: string }) => stat._id == 'gautas')[0].count ?? 0, - e.filter((stat: { _id: string }) => stat._id == 'tiriamas')[0].count ?? 0, - e.filter((stat: { _id: string }) => stat._id == 'ištirtas')[0].count ?? 0, - e.filter((stat: { _id: string }) => stat._id == 'sutvarkyta')[0].count ?? - 0, - e.filter((stat: { _id: string }) => stat._id == 'nepasitvirtino')[0] - .count ?? 0, + ReportService.filterStatistics(e, 'gautas'), + ReportService.filterStatistics(e, 'tiriamas'), + ReportService.filterStatistics(e, 'išspręsta'), + ReportService.filterStatistics(e, 'nepasitvirtino'), ); } + private static filterStatistics(e: any, status: string): number { + return e.filter((stat: { _id: string }) => stat._id == status).length > 0 + ? e.filter((stat: { _id: string }) => stat._id == status)[0].count ?? 0 + : 0; + } + private static docToPublicReport(e: Report): PublicReportDto { return new PublicReportDto( e.name, - e.type, + ReportService.parseReportCategory(e.type), e.refId, e.reportLong, e.reportLat, @@ -68,4 +76,12 @@ export class ReportService { ): StatusRecordsDto { return new StatusRecordsDto(e.status, new Date(e.date)); } + + private static parseReportCategory(value: string): ReportCategory { + if (Object.values(ReportCategory).includes(value as ReportCategory)) { + return value as ReportCategory; + } else { + throw new Error(`Invalid report category: ${value}`); + } + } } diff --git a/src/repositories/dumps/dump.repository.ts b/src/repositories/dumps/dump.repository.ts index e378df5..ba6d65e 100644 --- a/src/repositories/dumps/dump.repository.ts +++ b/src/repositories/dumps/dump.repository.ts @@ -1,7 +1,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { Dump } from './schemas'; -import { UpdateDumpDto } from '../../admin/dto/update-dump.dto'; +import { UpdateDumpDto } from '../../admin/dto'; import { CreateDumpDto } from '../../admin/dto'; export class DumpRepository { @@ -34,7 +34,7 @@ export class DumpRepository { return await newDump.save(); } getDumpById(id: string) { - return this.dumpModel.findOne({ _id: id }).exec(); + return this.dumpModel.findOne({ _id: { eq: id } }).exec(); } async updateDump(updateDump: UpdateDumpDto): Promise { const dump = await this.getDumpById(updateDump._id); diff --git a/src/repositories/reports/report.repository.ts b/src/repositories/reports/report.repository.ts index 3cdfcb6..954de09 100644 --- a/src/repositories/reports/report.repository.ts +++ b/src/repositories/reports/report.repository.ts @@ -7,6 +7,7 @@ import { CloudinaryService } from '../../cloudinary/cloudinary.service'; import { BadRequestException } from '@nestjs/common'; import { HistoryDataDto } from '../../admin/dto/history-data.dto'; import { HistoryEditsDto } from '../../admin/dto/history-edits.dto'; +import { ReportCategory } from '../../common/dto/report-category'; export class ReportRepository { constructor( @@ -14,39 +15,47 @@ export class ReportRepository { private cloudinary: CloudinaryService, ) {} - async getVisibleReports(): Promise { - return await this.reportModel - .find({ isVisible: true, isDeleted: false }) - .sort({ reportDate: -1 }) - .exec(); - } + getVisibleReports(category?: ReportCategory): Promise { + const query: any = { isVisible: true, isDeleted: false }; - async getAllReports(): Promise { - return await this.reportModel - .find({ isDeleted: false }) - .sort({ reportDate: -1 }) - .exec(); + if (category) { + query.type = { $eq: category }; + } + + return this.reportModel.find(query).sort({ reportDate: -1 }).exec(); } - async getDeletedReports(): Promise { - return await this.reportModel - .find({ isDeleted: true }) - .sort({ reportDate: -1 }) - .exec(); + getAllReports( + isDeleted: boolean, + category?: ReportCategory, + ): Promise { + const query: any = { isDeleted: isDeleted }; + + if (category) { + query.type = { $eq: category }; + } + + return this.reportModel.find(query).sort({ reportDate: -1 }).exec(); } async getReportById(refId: number): Promise { - return await this.reportModel.findOne({ refId: refId }).exec(); + return await this.reportModel.findOne({ refId: { $eq: refId } }).exec(); } - async getVisibleStatusCounts(): Promise { - return await this.reportModel + getVisibleStatusCounts(category?: ReportCategory): Promise { + const query: any = { + isDeleted: false, + isVisible: true, + }; + + if (category) { + query.type = { $eq: category }; + } + + return this.reportModel .aggregate([ { - $match: { - isDeleted: false, - isVisible: true, - }, + $match: query, }, { $group: { @@ -72,7 +81,7 @@ export class ReportRepository { const reportCount = reports.length; const newReport = new this.reportModel({ name: createReport.name, - type: 'trash', + type: createReport.category, refId: reportCount + 1, comment: ' ', reportLong: createReport.longitude, @@ -118,7 +127,7 @@ export class ReportRepository { } const report = await this.reportModel - .findOne({ refId: updateReport.refId }) + .findOne({ refId: { $eq: updateReport.refId } }) .exec(); if (report != null) { @@ -212,7 +221,7 @@ export class ReportRepository { updatedReport = await this.reportModel .findOneAndUpdate( { - refId: updateReport.refId, + refId: { $eq: updateReport.refId }, }, { $set: {