Merge pull request 'Fix ratings and statistics' (#3) from s.yahyaee/yara724-api:main into main

Reviewed-on: Yara724/api#3
This commit is contained in:
2026-01-31 17:43:20 +03:30
4 changed files with 164 additions and 1 deletions

View File

@@ -211,6 +211,9 @@ export class FileRating {
@Prop({ type: Number, min: 0, max: 5 })
guiltyVehicleIdentification: number;
@Prop({ type: Number, min: 0, max: 5 })
botRating: number;
}
export class PriceDrop {

View File

@@ -69,6 +69,11 @@ export class ExpertInsurerController {
);
}
@Get("statistics")
async getExpertStatistics(@CurrentUser() actor) {
return await this.expertInsurerService.getExpertStatisticsReport(actor);
}
@ApiParam({ name: "expertId" })
@ApiQuery({ name: "role", enum: ["claim", "blame"] })
@Get("/:expertId")
@@ -120,13 +125,28 @@ export class ExpertInsurerController {
example: 4,
description: "تشخیص درست وسیله نقلیه مقصر",
},
botRating: {
type: "number",
minimum: 0,
maximum: 5,
example: 4,
description: "برای امتیاز دادن به بات",
},
},
required: [
"collisionMethodAccuracy",
"evaluationTimeliness",
"accidentCauseAccuracy",
"guiltyVehicleIdentification",
"botRating",
],
example: {
collisionMethodAccuracy: 4,
evaluationTimeliness: 3,
accidentCauseAccuracy: 5,
guiltyVehicleIdentification: 4,
botRating: 4,
},
},
})
@ApiParam({ name: "requestId" })

View File

@@ -768,4 +768,141 @@ export class ExpertInsurerService {
return newBranch;
}
/**
* Get comprehensive statistics for all experts of a client
* Returns:
* - Percentage of insurer's rating to BOT
* - Percentage of expert's rating given by users
* - Percentage of files that have objection
* - Number of files created in the current month
*/
async getExpertStatisticsReport(actor: any) {
const { clientKey } = actor;
if (!clientKey) {
throw new BadRequestException("Client key is required");
}
const clientObjectId = new Types.ObjectId(clientKey);
// Get all claim files for this client
const claimFiles = await this.claimRequestManagementModel
.find({
userClientKey: clientObjectId,
})
.lean();
// Calculate current month date range
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
// Filter files created this month
const filesThisMonth = claimFiles.filter((file) => {
const createdAt = new Date(file.createdAt);
return createdAt >= monthStart && createdAt <= monthEnd;
});
// Calculate statistics
let totalInsurerRatings = 0;
let totalBotRatings = 0;
let filesWithInsurerRating = 0;
let filesWithBotRating = 0;
let filesWithUserRating = 0;
let filesWithObjection = 0;
for (const file of claimFiles) {
const insurerRating = file?.rating;
const userRating = file?.userRating;
const objection = file?.objection;
// Check for insurer rating (excluding botRating)
if (insurerRating) {
const insurerValues = [
insurerRating.collisionMethodAccuracy,
insurerRating.evaluationTimeliness,
insurerRating.accidentCauseAccuracy,
insurerRating.guiltyVehicleIdentification,
].filter((val): val is number => typeof val === "number" && !isNaN(val));
if (insurerValues.length > 0) {
filesWithInsurerRating++;
const insurerAvg =
insurerValues.reduce((a, b) => a + b, 0) / insurerValues.length;
totalInsurerRatings += insurerAvg;
}
}
// Check for bot rating (if botRating field exists in rating object)
const botRating = (insurerRating as any)?.botRating;
if (botRating !== undefined && !isNaN(botRating) && typeof botRating === "number") {
filesWithBotRating++;
totalBotRatings += botRating;
}
// Check for user rating
if (userRating) {
filesWithUserRating++;
}
// Check for objection
if (objection) {
filesWithObjection++;
}
}
// Calculate percentages
const totalFiles = claimFiles.length;
// Calculate average insurer rating (excluding botRating) and average bot rating
const averageInsurerRating =
filesWithInsurerRating > 0
? totalInsurerRatings / filesWithInsurerRating
: 0;
const averageBotRating =
filesWithBotRating > 0 ? totalBotRatings / filesWithBotRating : 0;
// Calculate percentage: (averageInsurerRating / averageBotRating) * 100
// This shows what percentage the insurer rating is compared to bot rating
const insurerToBotPercentage =
averageBotRating > 0 && averageInsurerRating > 0
? parseFloat(
((averageInsurerRating / averageBotRating) * 100).toFixed(2),
)
: 0;
const userRatingPercentage =
totalFiles > 0
? parseFloat(((filesWithUserRating / totalFiles) * 100).toFixed(2))
: 0;
const objectionPercentage =
totalFiles > 0
? parseFloat(((filesWithObjection / totalFiles) * 100).toFixed(2))
: 0;
return {
insurerToBotRatingPercentage: insurerToBotPercentage,
userRatingPercentage: userRatingPercentage,
objectionPercentage: objectionPercentage,
filesCreatedThisMonth: filesThisMonth.length,
totalFiles: totalFiles,
breakdown: {
filesWithInsurerRating,
filesWithBotRating,
filesWithUserRating,
filesWithObjection,
averageInsurerRating:
filesWithInsurerRating > 0
? parseFloat(
(totalInsurerRatings / filesWithInsurerRating).toFixed(2),
)
: 0,
averageBotRating:
filesWithBotRating > 0
? parseFloat((totalBotRatings / filesWithBotRating).toFixed(2))
: 0,
},
};
}
}

View File

@@ -346,6 +346,9 @@ export class FileRating {
@Prop({ type: Number, min: 0, max: 5 })
guiltyVehicleIdentification: number;
@Prop({ type: Number, min: 0, max: 5 })
botRating: number;
}
// TODO clean all sub Object to other files