YARA-739, YARA-740, YARA-741

This commit is contained in:
SepehrYahyaee
2026-01-28 15:34:38 +03:30
parent 8db56d38be
commit ae53ef6032
9 changed files with 305 additions and 25 deletions

View File

@@ -100,20 +100,61 @@ export class ExpertInsurerService {
Record<string, number[]>
> = {};
const processRatings = (expertId: string, rating: any) => {
if (!expertId || !rating || typeof rating !== "object") {
return;
}
/**
* Converts insurer FileRating and optional userRating into a single
* combined score for a file.
*/
const getCombinedFileScore = (file: any): number | null => {
const insurerRating = file?.rating;
const userRating = file?.userRating;
const allRatingValues = Object.values(rating).filter(
(val): val is number => typeof val === "number" && !isNaN(val),
const insurerValues = insurerRating
? Object.values(insurerRating).filter(
(val): val is number => typeof val === "number" && !isNaN(val),
)
: [];
const insurerAvg =
insurerValues.length > 0
? insurerValues.reduce((a, b) => a + b, 0) / insurerValues.length
: null;
const userValues = userRating
? [
userRating.progressSpeed,
userRating.registrationEase,
userRating.overallEvaluation,
].filter((val) => typeof val === "number" && !isNaN(val))
: [];
const userAvg =
userValues.length > 0
? userValues.reduce((a, b) => a + b, 0) / userValues.length
: null;
const scores = [insurerAvg, userAvg].filter(
(v): v is number => typeof v === "number" && !isNaN(v),
);
if (allRatingValues.length > 0) {
if (scores.length === 0) return null;
return parseFloat(
(scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(2),
);
};
const processRatings = (expertId: string, file: any) => {
if (!expertId) return;
const rating = file?.rating;
const combinedScore = getCombinedFileScore(file);
// Aggregate overall combined score for this expert
if (combinedScore !== null) {
if (!expertTotalRatingsMap[expertId])
expertTotalRatingsMap[expertId] = [];
expertTotalRatingsMap[expertId].push(...allRatingValues);
expertTotalRatingsMap[expertId].push(combinedScore);
}
// Keep backward-compatible per-category insurer ratings
if (!rating || typeof rating !== "object") return;
if (!expertRatingsByCategoryMap[expertId]) {
expertRatingsByCategoryMap[expertId] = {};
}
@@ -129,12 +170,12 @@ export class ExpertInsurerService {
for (const file of blameFiles) {
const expertId = file?.actorLocked?.actorId?.toString();
processRatings(expertId, file.rating);
processRatings(expertId, file);
}
for (const file of claimFiles) {
const expertId = file?.damageExpertReply?.actorDetail?.actorId;
processRatings(expertId, file.rating);
processRatings(expertId, file);
}
const allExperts = allExpertsRaw.map((expert) => {
@@ -182,6 +223,98 @@ export class ExpertInsurerService {
};
}
/**
* Returns top 10 experts for the current insurer client based on
* combined insurer + user ratings on their handled files.
*/
async getTopExpertsForClient(actor): Promise<any[]> {
const result = await this.retrieveAllExpertsOfClient(actor, 1, 1000);
const experts = result?.experts || [];
const sorted = [...experts].sort((a, b) => {
const ar = a.overallAverageRating ?? 0;
const br = b.overallAverageRating ?? 0;
return br - ar;
});
return sorted.slice(0, 10);
}
/**
* Returns top 10 claim files for the current insurer client based on
* combined insurer + user ratings.
*/
async getTopFilesForClient(insurerId: string): Promise<any[]> {
const id = new Types.ObjectId(insurerId);
const claimFiles = await this.claimRequestManagementModel
.find(
{
userClientKey: id,
},
{
requestNumber: 1,
userClientKey: 1,
userId: 1,
fullName: 1,
carDetail: 1,
carPlate: 1,
claimStatus: 1,
rating: 1,
userRating: 1,
damageExpertReply: 1,
damageExpertReplyFinal: 1,
createdAt: 1,
},
)
.lean();
const scored = claimFiles
.map((file) => {
const insurerRating = file?.rating;
const userRating = file?.userRating;
const insurerValues = insurerRating
? Object.values(insurerRating).filter(
(val): val is number => typeof val === "number" && !isNaN(val),
)
: [];
const insurerAvg =
insurerValues.length > 0
? insurerValues.reduce((a, b) => a + b, 0) / insurerValues.length
: null;
const userValues = userRating
? [
userRating.progressSpeed,
userRating.registrationEase,
userRating.overallEvaluation,
].filter((val) => typeof val === "number" && !isNaN(val))
: [];
const userAvg =
userValues.length > 0
? userValues.reduce((a, b) => a + b, 0) / userValues.length
: null;
const scores = [insurerAvg, userAvg].filter(
(v): v is number => typeof v === "number" && !isNaN(v),
);
if (scores.length === 0) return null;
const combinedScore =
scores.reduce((a, b) => a + b, 0) / scores.length;
return {
...file,
combinedScore: parseFloat(combinedScore.toFixed(2)),
};
})
.filter((f) => f !== null);
const sorted = scored.sort((a, b) => b.combinedScore - a.combinedScore);
return sorted.slice(0, 10);
}
async getAllFilesByExpertAndRole(expertId: string, role: "claim" | "blame") {
const expertObjectId = new Types.ObjectId(expertId);