added transaction for calculating the pqq final answer score

This commit is contained in:
2025-11-27 19:16:46 +05:30
parent b0bae33b6e
commit 326ea2f96b
3 changed files with 184 additions and 110 deletions

View File

@@ -39,7 +39,7 @@ interface HostDocumentInput {
@Injectable()
export class HostService {
constructor(private prisma: PrismaService) {}
constructor(private prisma: PrismaService) { }
async createHost(data: CreateHostDto) {
return this.prisma.user.create({ data });
@@ -720,119 +720,132 @@ export class HostService {
// }
async calculatePqqScoreForUser(activityXid: number) {
// 1. Get all headers for this activity (user's answers)
const answers = await this.prisma.activityPQQheader.findMany({
where: { activityXid },
include: {
pqqQuestions: {
include: {
pqqSubCategories: {
include: {
category: true,
return await this.prisma.$transaction(async (tx) => {
// 1. Get all headers for this activity (user's answers)
const answers = await this.prisma.activityPQQheader.findMany({
where: { activityXid },
include: {
pqqQuestions: {
include: {
pqqSubCategories: {
include: {
category: true,
},
},
},
},
pqqAnswers: true,
},
pqqAnswers: true,
},
});
});
if (!answers.length) {
return {
overallPercentage: 0,
categoryWise: {},
};
}
// Prepare accumulators
let totalUserPoints = 0;
let totalMaxPoints = 0;
// For category-wise scoring
const categories: Record<
number,
{
categoryId: number;
categoryName: string;
userPoints: number;
maxPoints: number;
}
> = {};
for (const item of answers) {
const question = item.pqqQuestions;
const answer = item.pqqAnswers;
const maxPoints = question.maxPoints;
const userPoints = answer.answerPoints;
totalUserPoints += userPoints;
totalMaxPoints += maxPoints;
// Category
const category = question.pqqSubCategories.category;
const categoryId = category.id;
if (!categories[categoryId]) {
categories[categoryId] = {
categoryId,
categoryName: category.categoryName,
userPoints: 0,
maxPoints: 0,
if (!answers.length) {
return {
overallPercentage: 0,
categoryWise: {},
};
}
categories[categoryId].userPoints += userPoints;
categories[categoryId].maxPoints += maxPoints;
}
// Prepare accumulators
let totalUserPoints = 0;
let totalMaxPoints = 0;
// Overall percent
const overallPercentage =
totalMaxPoints > 0 ? (totalUserPoints / totalMaxPoints) * 100 : 0;
// For category-wise scoring
const categories: Record<
number,
{
categoryId: number;
categoryName: string;
userPoints: number;
maxPoints: number;
}
> = {};
for (const item of answers) {
const question = item.pqqQuestions;
const answer = item.pqqAnswers;
const maxPoints = question.maxPoints;
const userPoints = answer.answerPoints;
totalUserPoints += userPoints;
totalMaxPoints += maxPoints;
// Category
const category = question.pqqSubCategories.category;
const categoryId = category.id;
if (!categories[categoryId]) {
categories[categoryId] = {
categoryId,
categoryName: category.categoryName,
userPoints: 0,
maxPoints: 0,
};
}
categories[categoryId].userPoints += userPoints;
categories[categoryId].maxPoints += maxPoints;
}
// Overall percent
const overallPercentage =
totalMaxPoints > 0 ? (totalUserPoints / totalMaxPoints) * 100 : 0;
if (overallPercentage > 50) {
await this.prisma.activities.update({
where: {
id: activityXid
},
data: {
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.UNDER_REVIEW,
activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.UNDER_REVIEW
}
})
} else {
await this.prisma.activities.update({
where: {
id: activityXid
},
data: {
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.PQQ_FAILED,
activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.PQQ_FAILED
}
})
}
// ---------- 🔥 ONLY FIRST 2 CATEGORIES ----------
const categoryArray = Object.values(categories);
// Sort by categoryId (or change to displayOrder if needed)
categoryArray.sort((a, b) => a.categoryId - b.categoryId);
// Take only first 2 categories
const topTwo = categoryArray.slice(0, 2);
const categoryWise: Record<string, number> = {};
for (const c of topTwo) {
categoryWise[c.categoryName] =
c.maxPoints > 0 ? (c.userPoints / c.maxPoints) * 100 : 0;
}
if (overallPercentage > 50) {
await this.prisma.activities.update({
where: {
id: activityXid
},
data: {
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.UNDER_REVIEW,
activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.UNDER_REVIEW
totalScore: overallPercentage,
sustainabilityScore: categoryWise.Sustainability,
safetyScore: categoryWise.Safety
}
})
} else {
await this.prisma.activities.update({
where: {
id: activityXid
},
data: {
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.PQQ_FAILED,
activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.PQQ_FAILED
}
})
}
// ---------- 🔥 ONLY FIRST 2 CATEGORIES ----------
const categoryArray = Object.values(categories);
// Sort by categoryId (or change to displayOrder if needed)
categoryArray.sort((a, b) => a.categoryId - b.categoryId);
// Take only first 2 categories
const topTwo = categoryArray.slice(0, 2);
const categoryWise: Record<string, number> = {};
for (const c of topTwo) {
categoryWise[c.categoryName] =
c.maxPoints > 0 ? (c.userPoints / c.maxPoints) * 100 : 0;
}
// Return final score object
return {
overallPercentage,
categoryWise,
};
// Return final score object
return {
overallPercentage,
categoryWise,
};
});
}
async createHeader(
@@ -927,10 +940,52 @@ export class HostService {
return await this.prisma.activityPQQheader.findMany({
where: { isActive: true },
include: {
pqqQuestions: true,
ActivityPQQSuggestions: true,
pqqAnswers: true,
ActivityPQQSupportings: true,
pqqQuestions: {
select: {
questionName: true,
maxPoints: true,
displayOrder: true,
pqqSubCategories: {
select: {
id: true,
subCategoryName: true,
displayOrder: true,
category: {
select: {
id: true,
categoryName: true,
displayOrder: true,
}
}
}
}
}
},
ActivityPQQSuggestions: {
select: {
id: true,
title: true,
comments: true,
isReviewed: true,
reviewedBy: true,
reviewedOn: true,
}
},
pqqAnswers: {
select: {
id: true,
displayOrder: true,
answerName: true,
answerPoints: true
}
},
ActivityPQQSupportings: {
select: {
id: true,
mediaFileName: true,
mediaType: true,
}
},
},
});
}

View File

@@ -1,11 +1,10 @@
import { verifyMinglarAdminToken } from '@/common/middlewares/jwt/authForMinglarAdmin';
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { PrismaService } from '../../../../../common/database/prisma.service';
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
import ApiError from '../../../../../common/utils/helper/ApiError';
import { string } from 'zod';
import { PrePopulateService } from '../../../../prepopulate/services/prepopulate.service';
import { MinglarService } from '../../../services/minglar.service';
import { verifyMinglarAdminHostToken } from '@/common/middlewares/jwt/authForMinglarAdmin&Host';
const prismaService = new PrismaService();
const minglarService = new MinglarService(prismaService);
@@ -27,7 +26,7 @@ export const handler = safeHandler(async (
}
// Verify token and get user info
const userInfo = await verifyMinglarAdminHostToken(token);
const userInfo = await verifyMinglarAdminToken(token);
const hostXid = Number(event.pathParameters?.id)

View File

@@ -23,7 +23,7 @@ import { sendAMEmailForHostAssign } from './AMEmail.service';
@Injectable()
export class MinglarService {
constructor(private prisma: PrismaService) {}
constructor(private prisma: PrismaService) { }
async createPassword(user_xid: number, password: string): Promise<boolean> {
// Find user by id
@@ -203,16 +203,36 @@ export class MinglarService {
}
async getAllHostActivityForMinglar(search?: string, hostXid?: number) {
return await this.prisma.activities.findMany({
return await this.prisma.activities.findMany({
where: {
isActive: true,
hostXid: hostXid,
},
include: {
ActivitiesMedia: true,
ActivityAmDetails: true,
ActivitiesMedia: {
select: {
id: true,
mediaFileName: true,
mediaType: true,
displayOrder: true,
}
},
ActivityAmDetails: {
select: {
accountManager: {
select: {
id: true,
firstName: true,
lastName: true,
profileImage: true,
emailAddress: true,
roleXid: true,
}
}
}
},
activityType: true,
},
},
});
}
@@ -614,7 +634,7 @@ export class MinglarService {
if (
userStatus &&
userStatus.trim().toLowerCase() ===
MINGLAR_STATUS_DISPLAY.NEW.toLowerCase()
MINGLAR_STATUS_DISPLAY.NEW.toLowerCase()
) {
filters.adminStatusInternal = MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW;
}
@@ -872,7 +892,7 @@ export class MinglarService {
if (
hostDetails.adminStatusInternal !==
MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED &&
MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED &&
hostDetails.adminStatusDisplay !== MINGLAR_STATUS_DISPLAY.AM_NOT_ASSIGNED
) {
throw new ApiError(400, 'Invalid host status');