224 lines
7.7 KiB
TypeScript
224 lines
7.7 KiB
TypeScript
import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
|
|
import { Inject, Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
import * as schema from 'src/database/index';
|
|
import { trainingSurveys } from 'src/database/index';
|
|
import { eq, like, or, and, gte, lte, SQL, sql } from 'drizzle-orm';
|
|
import { CreateTrainingDto } from './dto/create-training.dto';
|
|
import { UpdateTrainingDto } from './dto/update-training.dto';
|
|
import { TrainingStatisticsFilterDto } from './dto/training-statistics-filter.dto';
|
|
import { states } from 'src/database/index';
|
|
import { PaginationDto } from '../../common/dto/pagination.dto';
|
|
|
|
@Injectable()
|
|
export class TrainingService {
|
|
constructor(
|
|
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
|
|
) { }
|
|
|
|
|
|
async findAll(paginationDto?: PaginationDto) {
|
|
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
|
|
|
|
const offset = (page - 1) * limit;
|
|
|
|
let searchCondition: SQL<unknown> | undefined;
|
|
if (search) {
|
|
searchCondition = or(
|
|
like(trainingSurveys.firstname, `%${search}%`),
|
|
like(trainingSurveys.lastname, `%${search}%`),
|
|
like(trainingSurveys.ospName, `%${search}%`),
|
|
like(trainingSurveys.ospRif, `%${search}%`)
|
|
);
|
|
}
|
|
|
|
const orderBy = sortOrder === 'asc'
|
|
? sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} asc`
|
|
: sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} desc`;
|
|
|
|
const totalCountResult = await this.drizzle
|
|
.select({ count: sql<number>`count(*)` })
|
|
.from(trainingSurveys)
|
|
.where(searchCondition);
|
|
|
|
const totalCount = Number(totalCountResult[0].count);
|
|
const totalPages = Math.ceil(totalCount / limit);
|
|
|
|
const data = await this.drizzle
|
|
.select()
|
|
.from(trainingSurveys)
|
|
.where(searchCondition)
|
|
.orderBy(orderBy)
|
|
.limit(limit)
|
|
.offset(offset);
|
|
|
|
const meta = {
|
|
page,
|
|
limit,
|
|
totalCount,
|
|
totalPages,
|
|
hasNextPage: page < totalPages,
|
|
hasPreviousPage: page > 1,
|
|
nextPage: page < totalPages ? page + 1 : null,
|
|
previousPage: page > 1 ? page - 1 : null,
|
|
};
|
|
|
|
return { data, meta };
|
|
}
|
|
|
|
async getStatistics(filterDto: TrainingStatisticsFilterDto) {
|
|
const { startDate, endDate, stateId, municipalityId, parishId, ospType } = filterDto;
|
|
|
|
const filters: SQL[] = [];
|
|
|
|
if (startDate) {
|
|
filters.push(gte(trainingSurveys.visitDate, new Date(startDate)));
|
|
}
|
|
|
|
if (endDate) {
|
|
filters.push(lte(trainingSurveys.visitDate, new Date(endDate)));
|
|
}
|
|
|
|
if (stateId) {
|
|
filters.push(eq(trainingSurveys.state, stateId));
|
|
}
|
|
|
|
if (municipalityId) {
|
|
filters.push(eq(trainingSurveys.municipality, municipalityId));
|
|
}
|
|
|
|
if (parishId) {
|
|
filters.push(eq(trainingSurveys.parish, parishId));
|
|
}
|
|
|
|
if (ospType) {
|
|
filters.push(eq(trainingSurveys.ospType, ospType));
|
|
}
|
|
|
|
const whereCondition = filters.length > 0 ? and(...filters) : undefined;
|
|
|
|
const totalOspsResult = await this.drizzle
|
|
.select({ count: sql<number>`count(*)` })
|
|
.from(trainingSurveys)
|
|
.where(whereCondition);
|
|
const totalOsps = Number(totalOspsResult[0].count);
|
|
|
|
const totalProducersResult = await this.drizzle
|
|
.select({ sum: sql<number>`sum(${trainingSurveys.producerCount})` })
|
|
.from(trainingSurveys)
|
|
.where(whereCondition);
|
|
const totalProducers = Number(totalProducersResult[0].sum || 0);
|
|
|
|
const statusDistribution = await this.drizzle
|
|
.select({
|
|
name: trainingSurveys.currentStatus,
|
|
value: sql<number>`count(*)`
|
|
})
|
|
.from(trainingSurveys)
|
|
.where(whereCondition)
|
|
.groupBy(trainingSurveys.currentStatus);
|
|
|
|
const activityDistribution = await this.drizzle
|
|
.select({
|
|
name: trainingSurveys.productiveActivity,
|
|
value: sql<number>`count(*)`
|
|
})
|
|
.from(trainingSurveys)
|
|
.where(whereCondition)
|
|
.groupBy(trainingSurveys.productiveActivity);
|
|
|
|
const typeDistribution = await this.drizzle
|
|
.select({
|
|
name: trainingSurveys.ospType,
|
|
value: sql<number>`count(*)`
|
|
})
|
|
.from(trainingSurveys)
|
|
.where(whereCondition)
|
|
.groupBy(trainingSurveys.ospType);
|
|
|
|
// New Aggregations
|
|
const stateDistribution = await this.drizzle
|
|
.select({
|
|
name: states.name,
|
|
value: sql<number>`count(${trainingSurveys.id})`
|
|
})
|
|
.from(trainingSurveys)
|
|
.leftJoin(states, eq(trainingSurveys.state, states.id))
|
|
.where(whereCondition)
|
|
.groupBy(states.name);
|
|
|
|
const yearDistribution = await this.drizzle
|
|
.select({
|
|
name: sql<string>`cast(${trainingSurveys.companyConstitutionYear} as text)`,
|
|
value: sql<number>`count(*)`
|
|
})
|
|
.from(trainingSurveys)
|
|
.where(whereCondition)
|
|
.groupBy(trainingSurveys.companyConstitutionYear)
|
|
.orderBy(trainingSurveys.companyConstitutionYear);
|
|
|
|
return {
|
|
totalOsps,
|
|
totalProducers,
|
|
statusDistribution: statusDistribution.map(item => ({ ...item, value: Number(item.value) })),
|
|
activityDistribution: activityDistribution.map(item => ({ ...item, value: Number(item.value) })),
|
|
typeDistribution: typeDistribution.map(item => ({ ...item, value: Number(item.value) })),
|
|
stateDistribution: stateDistribution.map(item => ({ ...item, value: Number(item.value) })),
|
|
yearDistribution: yearDistribution.map(item => ({ ...item, value: Number(item.value) })),
|
|
};
|
|
}
|
|
|
|
async findOne(id: number) {
|
|
const find = await this.drizzle
|
|
.select()
|
|
.from(trainingSurveys)
|
|
.where(eq(trainingSurveys.id, id));
|
|
|
|
if (find.length === 0) {
|
|
throw new HttpException('Training record not found', HttpStatus.NOT_FOUND);
|
|
}
|
|
|
|
return find[0];
|
|
}
|
|
|
|
async create(createTrainingDto: CreateTrainingDto) {
|
|
const [newRecord] = await this.drizzle
|
|
.insert(trainingSurveys)
|
|
.values({
|
|
...createTrainingDto,
|
|
visitDate: new Date(createTrainingDto.visitDate),
|
|
})
|
|
.returning();
|
|
|
|
return newRecord;
|
|
}
|
|
|
|
async update(id: number, updateTrainingDto: UpdateTrainingDto) {
|
|
await this.findOne(id);
|
|
|
|
const updateData: any = { ...updateTrainingDto };
|
|
if (updateTrainingDto.visitDate) {
|
|
updateData.visitDate = new Date(updateTrainingDto.visitDate);
|
|
}
|
|
|
|
const [updatedRecord] = await this.drizzle
|
|
.update(trainingSurveys)
|
|
.set(updateData)
|
|
.where(eq(trainingSurveys.id, id))
|
|
.returning();
|
|
|
|
return updatedRecord;
|
|
}
|
|
|
|
async remove(id: number) {
|
|
await this.findOne(id);
|
|
|
|
const [deletedRecord] = await this.drizzle
|
|
.delete(trainingSurveys)
|
|
.where(eq(trainingSurveys.id, id))
|
|
.returning();
|
|
|
|
return { message: 'Training record deleted successfully', data: deletedRecord };
|
|
}
|
|
}
|