tango-app-api-trax 3.9.67 → 3.9.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -451,7 +451,7 @@ export async function redoChecklist( req, res ) {
|
|
|
451
451
|
|
|
452
452
|
let findQuestion = question[sectionIndex].questions.findIndex( ( ele ) => ele.qno == req.body.payload.qno );
|
|
453
453
|
|
|
454
|
-
let data = { ...question[sectionIndex].questions[findQuestion], redo: true, ...( !req.body.payload?.answerIndex && { redoComment: req.body.payload?.checklistDescription || '' } ) };
|
|
454
|
+
let data = { ...question[sectionIndex].questions[findQuestion], redo: true, redoCount: ( question[sectionIndex].questions[findQuestion]?.redoCount || 0 ) + 1, ...( !req.body.payload?.answerIndex && { redoComment: req.body.payload?.checklistDescription || '' } ) };
|
|
455
455
|
let answerIndex = req.body.payload?.answerIndex;
|
|
456
456
|
let isSingleAnswerRedo = answerIndex != null;
|
|
457
457
|
|
|
@@ -575,7 +575,7 @@ export async function redoChecklist( req, res ) {
|
|
|
575
575
|
type: checklistDetails.checkListType,
|
|
576
576
|
userAnswer: userAnswer,
|
|
577
577
|
initiatedBy: req.user.userName,
|
|
578
|
-
initiatedTime: dayjs
|
|
578
|
+
initiatedTime: dayjs().format(),
|
|
579
579
|
answerType: question[sectionIndex].questions[findQuestion].answerType,
|
|
580
580
|
submitedBy: checklistDetails.userName,
|
|
581
581
|
submitTime: dayjs.utc( checklistDetails.submitTime ).format(),
|
|
@@ -667,7 +667,7 @@ export async function redomultiChecklist( req, res ) {
|
|
|
667
667
|
|
|
668
668
|
let findQuestion = question[sectionIndex].questions.findIndex( ( ele ) => ele.qno == req.body.payload.qno );
|
|
669
669
|
|
|
670
|
-
let data = { ...question[sectionIndex].questions[findQuestion], redo: true, redoComment: req.body.payload?.showcomment ? req.body.payload?.checklistDescription : findcomment?.Comments };
|
|
670
|
+
let data = { ...question[sectionIndex].questions[findQuestion], redo: true, redoCount: ( question[sectionIndex].questions[findQuestion]?.redoCount || 0 ) + 1, redoComment: req.body.payload?.showcomment ? req.body.payload?.checklistDescription : findcomment?.Comments };
|
|
671
671
|
// if ( checklistDetails.client_id == '458' ) {
|
|
672
672
|
data.answers.forEach( ( item ) => {
|
|
673
673
|
if ( item.showLinked ) {
|
|
@@ -2669,7 +2669,12 @@ function QuestionFlag( req, res ) {
|
|
|
2669
2669
|
let answervalidate = question.userAnswer.filter( ( answer ) => {
|
|
2670
2670
|
return answer.sopFlag == true;
|
|
2671
2671
|
} );
|
|
2672
|
-
|
|
2672
|
+
let answerCount = 0;
|
|
2673
|
+
if ( answervalidate?.length ) {
|
|
2674
|
+
answerCount = 1;
|
|
2675
|
+
}
|
|
2676
|
+
// flagCount = flagCount + answervalidate.length;
|
|
2677
|
+
flagCount = flagCount + answerCount;
|
|
2673
2678
|
}
|
|
2674
2679
|
} );
|
|
2675
2680
|
} );
|
|
@@ -444,13 +444,47 @@ export const flagCardsV1 = async ( req, res ) => {
|
|
|
444
444
|
},
|
|
445
445
|
];
|
|
446
446
|
|
|
447
|
-
//
|
|
448
|
-
//
|
|
447
|
+
// Fast path: return only the Mongo-derived flag counts. The detection Lambda
|
|
448
|
+
// (slow) is fetched separately via flagCardsDetectionV1 and merged into these
|
|
449
|
+
// cards on the client once it resolves.
|
|
450
|
+
const getOverallChecklistData = await processedchecklistService.aggregate( findQuery );
|
|
451
|
+
|
|
452
|
+
if ( !getOverallChecklistData.length ) {
|
|
453
|
+
return res.sendSuccess( { flagCards } );
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const [ data ] = getOverallChecklistData;
|
|
457
|
+
if ( data.questionFlag ) flagCards.questionFlag.count = data.questionFlag;
|
|
458
|
+
if ( data.delayInSubmission ) flagCards.delayInSubmission.count = data.delayInSubmission;
|
|
459
|
+
flagCards.totalFlag = flagCards.questionFlag.count + flagCards.delayInSubmission.count;
|
|
460
|
+
|
|
461
|
+
return res.sendSuccess( { flagCards } );
|
|
462
|
+
} catch ( error ) {
|
|
463
|
+
console.error( 'Error in flagCardsV1:', error );
|
|
464
|
+
logger.error( { error, function: 'flagCardsV1' } );
|
|
465
|
+
return res.sendError( error, 500 );
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// Detection-only companion for flagCardsV1. Runs the published-checklist lookup
|
|
470
|
+
// + the detection Lambda (the slow part) and returns just the detection
|
|
471
|
+
// contribution, so the UI can merge it into the cards returned by flagCardsV1:
|
|
472
|
+
// flagCards.detectionFlag = res.flagCards.detectionFlag;
|
|
473
|
+
// flagCards.runAIFlag = res.flagCards.runAIFlag; // when present
|
|
474
|
+
// flagCards.totalFlag += res.flagCards.detectionTotalFlag;
|
|
475
|
+
export const flagCardsDetectionV1 = async ( req, res ) => {
|
|
476
|
+
try {
|
|
477
|
+
const { clientId, storeId, fromDate, toDate } = req.body;
|
|
478
|
+
|
|
479
|
+
const flagCards = {
|
|
480
|
+
detectionFlag: { count: 0 },
|
|
481
|
+
detectionTotalFlag: 0,
|
|
482
|
+
};
|
|
483
|
+
|
|
449
484
|
const detectionPayload = { fromDate, toDate, storeId, clientId };
|
|
450
485
|
const lambdaURL = 'https://f65azvtljclaxp6l7rnx65cdmm0lcgvp.lambda-url.ap-south-1.on.aws/';
|
|
451
486
|
|
|
452
|
-
const [
|
|
453
|
-
processedchecklistService.aggregate( findQuery ),
|
|
487
|
+
const [ publishedAiChecklists, resultData ] = await Promise.all( [
|
|
454
488
|
checklistconfigService.find(
|
|
455
489
|
{
|
|
456
490
|
client_id: clientId,
|
|
@@ -462,15 +496,6 @@ export const flagCardsV1 = async ( req, res ) => {
|
|
|
462
496
|
LamdaServiceCall( lambdaURL, detectionPayload ),
|
|
463
497
|
] );
|
|
464
498
|
|
|
465
|
-
if ( !getOverallChecklistData.length ) {
|
|
466
|
-
return res.sendSuccess( { flagCards } );
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const [ data ] = getOverallChecklistData;
|
|
470
|
-
if ( data.questionFlag ) flagCards.questionFlag.count = data.questionFlag;
|
|
471
|
-
if ( data.delayInSubmission ) flagCards.delayInSubmission.count = data.delayInSubmission;
|
|
472
|
-
flagCards.totalFlag = flagCards.questionFlag.count + flagCards.delayInSubmission.count;
|
|
473
|
-
|
|
474
499
|
const published = publishedAiChecklists.map( ( val ) => val?.checkListType );
|
|
475
500
|
if ( resultData && resultData.status_code === '200' ) {
|
|
476
501
|
published.forEach( ( item ) => {
|
|
@@ -478,18 +503,18 @@ export const flagCardsV1 = async ( req, res ) => {
|
|
|
478
503
|
flagCards.detectionFlag.count += resultData[item];
|
|
479
504
|
}
|
|
480
505
|
} );
|
|
481
|
-
flagCards.
|
|
506
|
+
flagCards.detectionTotalFlag += flagCards.detectionFlag.count;
|
|
482
507
|
if ( resultData?.runAI ) {
|
|
483
508
|
resultData.runAI.flagCount = resultData.runaiflags;
|
|
484
509
|
flagCards.runAIFlag = resultData?.runAI;
|
|
485
|
-
flagCards.
|
|
510
|
+
flagCards.detectionTotalFlag += resultData.runaiflags;
|
|
486
511
|
}
|
|
487
512
|
}
|
|
488
513
|
|
|
489
514
|
return res.sendSuccess( { flagCards } );
|
|
490
515
|
} catch ( error ) {
|
|
491
|
-
console.error( 'Error in
|
|
492
|
-
logger.error( { error, function: '
|
|
516
|
+
console.error( 'Error in flagCardsDetectionV1:', error );
|
|
517
|
+
logger.error( { error, function: 'flagCardsDetectionV1' } );
|
|
493
518
|
return res.sendError( error, 500 );
|
|
494
519
|
}
|
|
495
520
|
};
|
|
@@ -1996,6 +2021,151 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
|
|
|
1996
2021
|
return { comparisonData, ComparisonFlag };
|
|
1997
2022
|
};
|
|
1998
2023
|
|
|
2024
|
+
// Fast path: only the Mongo aggregations + client flag. The detection Lambda
|
|
2025
|
+
// (slow) is fetched separately via flagComparisonCardsDetectionV2, which
|
|
2026
|
+
// returns the total/detection/runAI comparisons for the UI to overwrite.
|
|
2027
|
+
const [ rangeOneData, rangeTwoData, traxMonthlyComparisonData ] = await Promise.all( [
|
|
2028
|
+
processedchecklistService.aggregate( createFindQuery( rangeOneFromDate, rangeOneToDate ) ),
|
|
2029
|
+
processedchecklistService.aggregate( createFindQuery( rangeTwoFromDate, rangeTwoToDate ) ),
|
|
2030
|
+
clientService.findOne( { clientId: requestData.clientId }, { traxMonthlyComparison: 1 } ),
|
|
2031
|
+
] );
|
|
2032
|
+
|
|
2033
|
+
// console.log( 'rangeOneFromDate =>', rangeOneFromDate );
|
|
2034
|
+
// console.log( 'rangeOneToDate =>', rangeOneToDate );
|
|
2035
|
+
// console.log( 'rangeOneData =>', rangeOneData );
|
|
2036
|
+
|
|
2037
|
+
// console.log( 'rangeTwoFromDate =>', rangeTwoFromDate );
|
|
2038
|
+
// console.log( 'rangeTwoToDate =>', rangeTwoToDate );
|
|
2039
|
+
// console.log( 'rangeTwoData =>', rangeTwoData );
|
|
2040
|
+
|
|
2041
|
+
const flagComparisonCards = {
|
|
2042
|
+
totalComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2043
|
+
questionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2044
|
+
delayInSubmissionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2045
|
+
detectionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2046
|
+
runAIComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
if ( rangeOneData.length && rangeTwoData.length ) {
|
|
2050
|
+
// Mongo-only total (question + delay). flagComparisonCardsDetectionV2 returns
|
|
2051
|
+
// the detection-inclusive total plus the detection/runAI comparisons, which
|
|
2052
|
+
// the UI overwrites onto these cards once the Lambda resolves.
|
|
2053
|
+
flagComparisonCards.totalComparisonFlag = calculateComparison( rangeOneData[0].totalFlag, rangeTwoData[0].totalFlag );
|
|
2054
|
+
flagComparisonCards.questionComparisonFlag = calculateComparison( rangeOneData[0].questionFlag, rangeTwoData[0].questionFlag );
|
|
2055
|
+
flagComparisonCards.delayInSubmissionComparisonFlag = calculateComparison( rangeOneData[0].delayInSubmission, rangeTwoData[0].delayInSubmission );
|
|
2056
|
+
}
|
|
2057
|
+
flagComparisonCards.traxMonthlyComparison = false;
|
|
2058
|
+
if ( traxMonthlyComparisonData && traxMonthlyComparisonData.traxMonthlyComparison ) {
|
|
2059
|
+
flagComparisonCards.traxMonthlyComparison = traxMonthlyComparisonData.traxMonthlyComparison;
|
|
2060
|
+
}
|
|
2061
|
+
return res.sendSuccess( { flagComparisonCards } );
|
|
2062
|
+
} catch ( error ) {
|
|
2063
|
+
console.log( 'error =>', error );
|
|
2064
|
+
logger.error( { error, function: 'flagComparisonCardsV2' } );
|
|
2065
|
+
return res.sendError( error, 500 );
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
|
|
2069
|
+
// Detection-only companion for flagComparisonCardsV2. Re-runs the (cheap) range
|
|
2070
|
+
// aggregations alongside the two detection Lambda calls, and returns only the
|
|
2071
|
+
// comparisons that depend on detection data. The UI overwrites these three keys
|
|
2072
|
+
// onto the cards from flagComparisonCardsV2 once this resolves:
|
|
2073
|
+
// totalComparisonFlag, detectionComparisonFlag, runAIComparisonFlag.
|
|
2074
|
+
// NOTE: the date-range / createFindQuery logic below mirrors flagComparisonCardsV2 —
|
|
2075
|
+
// keep the two in sync if that logic changes.
|
|
2076
|
+
export const flagComparisonCardsDetectionV2 = async ( req, res ) => {
|
|
2077
|
+
try {
|
|
2078
|
+
let requestData = req.body;
|
|
2079
|
+
let toDate = new Date( requestData.toDate );
|
|
2080
|
+
let userTimezoneOffset = toDate.getTimezoneOffset() * 60000;
|
|
2081
|
+
toDate = new Date( toDate.getTime() - userTimezoneOffset );
|
|
2082
|
+
toDate.setUTCHours( 23, 59, 59, 59 );
|
|
2083
|
+
|
|
2084
|
+
let rangeOneFromDate;
|
|
2085
|
+
let rangeOneToDate;
|
|
2086
|
+
let rangeTwoFromDate;
|
|
2087
|
+
let rangeTwoToDate;
|
|
2088
|
+
if ( requestData.dateType == 'weekly' ) {
|
|
2089
|
+
rangeOneFromDate = new Date( requestData.toDate );
|
|
2090
|
+
rangeOneFromDate.setDate( rangeOneFromDate.getDate() - 6 );
|
|
2091
|
+
rangeOneToDate = new Date( requestData.toDate );
|
|
2092
|
+
rangeOneToDate = new Date( rangeOneToDate.getTime() - userTimezoneOffset );
|
|
2093
|
+
rangeOneToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2094
|
+
rangeTwoToDate = new Date( requestData.toDate );
|
|
2095
|
+
rangeTwoToDate.setDate( rangeTwoToDate.getDate() - 7 );
|
|
2096
|
+
rangeTwoFromDate = new Date( rangeTwoToDate );
|
|
2097
|
+
rangeTwoFromDate.setDate( rangeTwoFromDate.getDate() - 6 );
|
|
2098
|
+
rangeTwoToDate = new Date( rangeTwoToDate.getTime() - userTimezoneOffset );
|
|
2099
|
+
rangeTwoToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2100
|
+
} else if ( requestData.dateType == 'monthly' ) {
|
|
2101
|
+
rangeOneFromDate = new Date( requestData.toDate );
|
|
2102
|
+
rangeOneFromDate.setDate( rangeOneFromDate.getDate() - 29 );
|
|
2103
|
+
rangeOneToDate = new Date( requestData.toDate );
|
|
2104
|
+
rangeOneToDate = new Date( rangeOneToDate.getTime() - userTimezoneOffset );
|
|
2105
|
+
rangeOneToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2106
|
+
rangeTwoToDate = new Date( requestData.toDate );
|
|
2107
|
+
rangeTwoToDate.setDate( rangeTwoToDate.getDate() - 30 );
|
|
2108
|
+
rangeTwoFromDate = new Date( rangeTwoToDate );
|
|
2109
|
+
rangeTwoFromDate.setDate( rangeTwoFromDate.getDate() - 29 );
|
|
2110
|
+
rangeTwoToDate = new Date( rangeTwoToDate.getTime() - userTimezoneOffset );
|
|
2111
|
+
rangeTwoToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2112
|
+
} else {
|
|
2113
|
+
rangeOneFromDate = new Date( requestData.toDate );
|
|
2114
|
+
rangeOneFromDate.setDate( rangeOneFromDate.getDate() - 0 );
|
|
2115
|
+
rangeOneToDate = new Date( requestData.toDate );
|
|
2116
|
+
rangeOneToDate = new Date( rangeOneToDate.getTime() - userTimezoneOffset );
|
|
2117
|
+
rangeOneToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2118
|
+
rangeTwoToDate = new Date( requestData.toDate );
|
|
2119
|
+
rangeTwoToDate.setDate( rangeTwoToDate.getDate() - 1 );
|
|
2120
|
+
rangeTwoFromDate = new Date( rangeTwoToDate );
|
|
2121
|
+
rangeTwoFromDate.setDate( rangeTwoFromDate.getDate() - 0 );
|
|
2122
|
+
rangeTwoToDate = new Date( rangeTwoToDate.getTime() - userTimezoneOffset );
|
|
2123
|
+
rangeTwoToDate.setUTCHours( 23, 59, 59, 59 );
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
const createFindQuery = ( fromDate, toDate ) => [
|
|
2127
|
+
{ $match: { client_id: requestData.clientId,
|
|
2128
|
+
date_iso: { $gte: fromDate, $lte: toDate },
|
|
2129
|
+
$or: requestData.clientId === '11' ?
|
|
2130
|
+
[
|
|
2131
|
+
{ store_id: { $in: requestData.storeId } },
|
|
2132
|
+
{ store_id: { $eq: '' }, userEmail: { $in: requestData.userEmailes } },
|
|
2133
|
+
] :
|
|
2134
|
+
[
|
|
2135
|
+
{ store_id: { $in: requestData.storeId } },
|
|
2136
|
+
{ store_id: { $eq: '' }, userEmail: { $in: requestData.userEmailes } },
|
|
2137
|
+
{ aiStoreList: { $in: requestData.storeId } },
|
|
2138
|
+
],
|
|
2139
|
+
} },
|
|
2140
|
+
{
|
|
2141
|
+
$project: {
|
|
2142
|
+
timeFlag: 1,
|
|
2143
|
+
questionFlag: 1,
|
|
2144
|
+
},
|
|
2145
|
+
},
|
|
2146
|
+
{
|
|
2147
|
+
$group: {
|
|
2148
|
+
_id: '',
|
|
2149
|
+
totalFlag: { $sum: { $add: [ '$questionFlag', '$timeFlag' ] } },
|
|
2150
|
+
questionFlag: { $sum: '$questionFlag' },
|
|
2151
|
+
delayInSubmission: { $sum: '$timeFlag' },
|
|
2152
|
+
},
|
|
2153
|
+
},
|
|
2154
|
+
];
|
|
2155
|
+
|
|
2156
|
+
const calculateComparison = ( rangeOneVal, rangeTwoVal ) => {
|
|
2157
|
+
if ( rangeOneVal === 0 ) {
|
|
2158
|
+
return { comparisonData: 0, ComparisonFlag: false };
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
let comparisonData = Math.abs( ( ( rangeOneVal - rangeTwoVal ) / rangeTwoVal ) * 100 );
|
|
2162
|
+
if ( comparisonData === Infinity ) comparisonData = 0;
|
|
2163
|
+
comparisonData = comparisonData % 1 === 0 ? comparisonData.toFixed( 0 ) : comparisonData.toFixed( 1 );
|
|
2164
|
+
comparisonData = parseInt( comparisonData );
|
|
2165
|
+
const ComparisonFlag = rangeOneVal > rangeTwoVal;
|
|
2166
|
+
return { comparisonData, ComparisonFlag };
|
|
2167
|
+
};
|
|
2168
|
+
|
|
1999
2169
|
const getDetectionCount = async ( fromDate, toDate ) => {
|
|
2000
2170
|
const detectionPayload = {
|
|
2001
2171
|
fromDate: dayjs( fromDate ).format( 'YYYY-MM-DD' ),
|
|
@@ -2004,7 +2174,6 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
|
|
|
2004
2174
|
clientId: requestData.clientId,
|
|
2005
2175
|
};
|
|
2006
2176
|
|
|
2007
|
-
// The config lookup and the detection Lambda are independent — run them together.
|
|
2008
2177
|
const LamdaURL = 'https://f65azvtljclaxp6l7rnx65cdmm0lcgvp.lambda-url.ap-south-1.on.aws/';
|
|
2009
2178
|
const [ publishedAiChecklists, resultData ] = await Promise.all( [
|
|
2010
2179
|
checklistconfigService.find(
|
|
@@ -2017,9 +2186,6 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
|
|
|
2017
2186
|
),
|
|
2018
2187
|
LamdaServiceCall( LamdaURL, detectionPayload ),
|
|
2019
2188
|
] );
|
|
2020
|
-
// if ( !publishedAiChecklists?.length ) {
|
|
2021
|
-
// return 0;
|
|
2022
|
-
// }
|
|
2023
2189
|
const published = publishedAiChecklists.map( ( val ) => val?.checkListType );
|
|
2024
2190
|
if ( resultData?.status_code === '200' ) {
|
|
2025
2191
|
let result = 0;
|
|
@@ -2037,51 +2203,268 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
|
|
|
2037
2203
|
return 0;
|
|
2038
2204
|
};
|
|
2039
2205
|
|
|
2040
|
-
const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount
|
|
2206
|
+
const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount ] = await Promise.all( [
|
|
2041
2207
|
processedchecklistService.aggregate( createFindQuery( rangeOneFromDate, rangeOneToDate ) ),
|
|
2042
2208
|
processedchecklistService.aggregate( createFindQuery( rangeTwoFromDate, rangeTwoToDate ) ),
|
|
2043
2209
|
getDetectionCount( rangeOneFromDate, rangeOneToDate ),
|
|
2044
2210
|
getDetectionCount( rangeTwoFromDate, rangeTwoToDate ),
|
|
2045
|
-
clientService.findOne( { clientId: requestData.clientId }, { traxMonthlyComparison: 1 } ),
|
|
2046
2211
|
] );
|
|
2047
2212
|
|
|
2048
|
-
// console.log( 'rangeOneFromDate =>', rangeOneFromDate );
|
|
2049
|
-
// console.log( 'rangeOneToDate =>', rangeOneToDate );
|
|
2050
|
-
// console.log( 'rangeOneData =>', rangeOneData );
|
|
2051
|
-
|
|
2052
|
-
// console.log( 'rangeTwoFromDate =>', rangeTwoFromDate );
|
|
2053
|
-
// console.log( 'rangeTwoToDate =>', rangeTwoToDate );
|
|
2054
|
-
// console.log( 'rangeTwoData =>', rangeTwoData );
|
|
2055
|
-
|
|
2056
2213
|
const flagComparisonCards = {
|
|
2057
2214
|
totalComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2058
|
-
questionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2059
|
-
delayInSubmissionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2060
2215
|
detectionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2061
2216
|
runAIComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
|
|
2062
2217
|
};
|
|
2063
2218
|
|
|
2064
2219
|
if ( rangeOneData.length && rangeTwoData.length ) {
|
|
2065
|
-
|
|
2066
|
-
|
|
2220
|
+
// Guard against detection failure (getDetectionCount returns 0, not an object).
|
|
2221
|
+
const rangeOneDetection = ( rangeOneDetectionCount && rangeOneDetectionCount.result ) || 0;
|
|
2222
|
+
const rangeTwoDetection = ( rangeTwoDetectionCount && rangeTwoDetectionCount.result ) || 0;
|
|
2223
|
+
const rangeOneAi = ( rangeOneDetectionCount && rangeOneDetectionCount.ai ) || 0;
|
|
2224
|
+
const rangeTwoAi = ( rangeTwoDetectionCount && rangeTwoDetectionCount.ai ) || 0;
|
|
2225
|
+
|
|
2226
|
+
const rangeOneTotalFlag = rangeOneData[0].totalFlag + rangeOneDetection;
|
|
2227
|
+
const rangeTwoTotalFlag = rangeTwoData[0].totalFlag + rangeTwoDetection;
|
|
2067
2228
|
flagComparisonCards.totalComparisonFlag = calculateComparison( rangeOneTotalFlag, rangeTwoTotalFlag );
|
|
2068
|
-
flagComparisonCards.
|
|
2069
|
-
flagComparisonCards.
|
|
2070
|
-
flagComparisonCards.detectionComparisonFlag = calculateComparison( rangeOneDetectionCount.result, rangeTwoDetectionCount.result );
|
|
2071
|
-
flagComparisonCards.runAIComparisonFlag = calculateComparison( rangeOneDetectionCount.ai, rangeTwoDetectionCount.ai );
|
|
2072
|
-
}
|
|
2073
|
-
flagComparisonCards.traxMonthlyComparison = false;
|
|
2074
|
-
if ( traxMonthlyComparisonData && traxMonthlyComparisonData.traxMonthlyComparison ) {
|
|
2075
|
-
flagComparisonCards.traxMonthlyComparison = traxMonthlyComparisonData.traxMonthlyComparison;
|
|
2229
|
+
flagComparisonCards.detectionComparisonFlag = calculateComparison( rangeOneDetection, rangeTwoDetection );
|
|
2230
|
+
flagComparisonCards.runAIComparisonFlag = calculateComparison( rangeOneAi, rangeTwoAi );
|
|
2076
2231
|
}
|
|
2232
|
+
|
|
2077
2233
|
return res.sendSuccess( { flagComparisonCards } );
|
|
2078
2234
|
} catch ( error ) {
|
|
2079
2235
|
console.log( 'error =>', error );
|
|
2080
|
-
logger.error( { error, function: '
|
|
2236
|
+
logger.error( { error, function: 'flagComparisonCardsDetectionV2' } );
|
|
2081
2237
|
return res.sendError( error, 500 );
|
|
2082
2238
|
}
|
|
2083
2239
|
};
|
|
2084
2240
|
|
|
2241
|
+
// Builds the per-checklist aggregation pipeline for the flag table. Used by the
|
|
2242
|
+
// detection endpoint (flagTablesDetectionV2) and mirrored by flagTablesV2's own
|
|
2243
|
+
// inline build — the two MUST produce an identical pipeline so the detection map
|
|
2244
|
+
// keys line up with the rows the client already rendered from flagTablesV2.
|
|
2245
|
+
function buildFlagTablesFindQuery( requestData ) {
|
|
2246
|
+
let fromDate = new Date( requestData.fromDate );
|
|
2247
|
+
let toDate = new Date( requestData.toDate );
|
|
2248
|
+
let userTimezoneOffset = toDate.getTimezoneOffset() * 60000;
|
|
2249
|
+
toDate = new Date( toDate.getTime() - userTimezoneOffset );
|
|
2250
|
+
toDate.setUTCHours( 23, 59, 59, 59 );
|
|
2251
|
+
|
|
2252
|
+
const findQuery = [];
|
|
2253
|
+
const findAndQuery = [];
|
|
2254
|
+
findAndQuery.push(
|
|
2255
|
+
{ client_id: requestData.clientId },
|
|
2256
|
+
{ date_iso: { $gte: fromDate, $lte: toDate } },
|
|
2257
|
+
{ $or: [ { store_id: { $in: requestData.storeId } }, { store_id: { $eq: '' }, userEmail: { $in: requestData.userEmailes } }, { aiStoreList: { $in: requestData.storeId } } ] },
|
|
2258
|
+
);
|
|
2259
|
+
|
|
2260
|
+
if ( requestData?.filter === 'all' ) {
|
|
2261
|
+
findAndQuery.push( { $or: [ { checkListType: { $in: [ 'custom', 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection', 'vehicle_check_in', 'employeemonitoring', 'activitymonitoring' ] } } ] } );
|
|
2262
|
+
} else if ( requestData?.filter === 'question' ) {
|
|
2263
|
+
findAndQuery.push( { checkListType: 'custom' } );
|
|
2264
|
+
findAndQuery.push( { questionFlag: { $gte: 1 } } );
|
|
2265
|
+
} else if ( requestData?.filter === 'time' ) {
|
|
2266
|
+
findAndQuery.push( { checkListType: 'custom' } );
|
|
2267
|
+
findAndQuery.push( { timeFlag: { $gte: 1 } } );
|
|
2268
|
+
} else if ( requestData?.filter === 'detection' ) {
|
|
2269
|
+
findAndQuery.push( { checkListType: { $ne: 'custom' } } );
|
|
2270
|
+
} else if ( requestData?.filter === 'runAI' ) {
|
|
2271
|
+
if ( requestData.runAIChecklistName ) {
|
|
2272
|
+
findAndQuery.push( { checkListName: { $in: requestData.runAIChecklistName } } );
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
findQuery.push( { $match: { $and: findAndQuery } } );
|
|
2277
|
+
|
|
2278
|
+
if ( requestData.searchValue && requestData.searchValue != '' ) {
|
|
2279
|
+
findQuery.push( { $match: { $or: [ { checkListName: { $regex: requestData.searchValue, $options: 'i' } } ] } } );
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
findQuery.push( {
|
|
2283
|
+
$project: {
|
|
2284
|
+
sourceCheckList_id: 1,
|
|
2285
|
+
checkListId: 1,
|
|
2286
|
+
checkListName: 1,
|
|
2287
|
+
coverage: 1,
|
|
2288
|
+
storeCount: 1,
|
|
2289
|
+
createdBy: 1,
|
|
2290
|
+
createdByName: 1,
|
|
2291
|
+
checklistStatus: 1,
|
|
2292
|
+
timeFlag: 1,
|
|
2293
|
+
questionFlag: 1,
|
|
2294
|
+
questionCount: 1,
|
|
2295
|
+
mobileDetectionFlag: 1,
|
|
2296
|
+
storeOpenCloseFlag: 1,
|
|
2297
|
+
uniformDetectionFlag: 1,
|
|
2298
|
+
checkListType: 1,
|
|
2299
|
+
scheduleRepeatedType: 1,
|
|
2300
|
+
store_id: 1,
|
|
2301
|
+
aiStoreList: 1,
|
|
2302
|
+
runAIQuestionCount: 1,
|
|
2303
|
+
},
|
|
2304
|
+
} );
|
|
2305
|
+
|
|
2306
|
+
findQuery.push( {
|
|
2307
|
+
$group: {
|
|
2308
|
+
_id: '$sourceCheckList_id',
|
|
2309
|
+
checkListName: { $last: '$checkListName' },
|
|
2310
|
+
checkListChar: { $last: { $substr: [ '$checkListName', 0, 2 ] } },
|
|
2311
|
+
coverage: { $last: '$coverage' },
|
|
2312
|
+
sourceCheckList_id: { $last: '$sourceCheckList_id' },
|
|
2313
|
+
checkListType: { $last: '$checkListType' },
|
|
2314
|
+
storeCount: { $sum: 1 },
|
|
2315
|
+
storeCountAi: { $max: '$storeCount' },
|
|
2316
|
+
flaggedStores: { $sum: { $cond: [
|
|
2317
|
+
{
|
|
2318
|
+
$or: [
|
|
2319
|
+
{ '$gt': [ '$timeFlag', 0 ] },
|
|
2320
|
+
{ '$gt': [ '$questionFlag', 0 ] },
|
|
2321
|
+
{ '$gt': [ '$mobileDetectionFlag', 0 ] },
|
|
2322
|
+
{ '$gt': [ '$storeOpenCloseFlag', 0 ] },
|
|
2323
|
+
{ '$gt': [ '$uniformDetectionFlag', 0 ] },
|
|
2324
|
+
{ '$gt': [ '$customerUnattended', 0 ] },
|
|
2325
|
+
{ '$gt': [ '$staffLeftInTheMiddle', 0 ] },
|
|
2326
|
+
{ '$gt': [ '$cleaning', 0 ] },
|
|
2327
|
+
{ '$gt': [ '$scrum', 0 ] },
|
|
2328
|
+
{ '$gt': [ '$employeeCount', 0 ] },
|
|
2329
|
+
{ '$gt': [ '$employeemonitoring', 0 ] },
|
|
2330
|
+
{ '$gt': [ '$activitymonitoring', 0 ] },
|
|
2331
|
+
],
|
|
2332
|
+
},
|
|
2333
|
+
1,
|
|
2334
|
+
0,
|
|
2335
|
+
] } },
|
|
2336
|
+
flagCount: {
|
|
2337
|
+
$sum: {
|
|
2338
|
+
$add: [ '$questionFlag', '$timeFlag' ],
|
|
2339
|
+
},
|
|
2340
|
+
},
|
|
2341
|
+
submittedChecklist: {
|
|
2342
|
+
$sum: {
|
|
2343
|
+
$cond: [ { $eq: [ '$checklistStatus', 'submit' ] }, 1, 0 ],
|
|
2344
|
+
},
|
|
2345
|
+
},
|
|
2346
|
+
questionFlag: { $sum: '$questionFlag' },
|
|
2347
|
+
timeFlag: { $sum: '$timeFlag' },
|
|
2348
|
+
questionCount: { $sum: '$questionCount' },
|
|
2349
|
+
aiStoreList: { $max: '$aiStoreList' },
|
|
2350
|
+
aiStoreListNew: { $sum: { $size: '$aiStoreList' } },
|
|
2351
|
+
submittedQuestionCount: { $sum: { $cond: [ { $eq: [ '$checklistStatus', 'submit' ] }, '$questionCount', 0 ] } },
|
|
2352
|
+
runAIQuestionCount: { $last: '$runAIQuestionCount' },
|
|
2353
|
+
},
|
|
2354
|
+
} );
|
|
2355
|
+
|
|
2356
|
+
findQuery.push( {
|
|
2357
|
+
$project: {
|
|
2358
|
+
assignedStores: '$storeCount',
|
|
2359
|
+
assignedStoresAi: '$aiStoreListNew',
|
|
2360
|
+
checkListName: 1,
|
|
2361
|
+
coverage: 1,
|
|
2362
|
+
checkListChar: 1,
|
|
2363
|
+
sourceCheckList_id: 1,
|
|
2364
|
+
checkListType: 1,
|
|
2365
|
+
flagType: 1,
|
|
2366
|
+
uniqueFlaggedStores: 1,
|
|
2367
|
+
flaggedStores: '$flaggedStores',
|
|
2368
|
+
flagCount: 1,
|
|
2369
|
+
questionCount: 1,
|
|
2370
|
+
correctAnswers: { $subtract: [ '$submittedQuestionCount', '$questionFlag' ] },
|
|
2371
|
+
customQuestionFlagCount: '$questionFlag',
|
|
2372
|
+
customTimeFlagCount: '$timeFlag',
|
|
2373
|
+
submittedChecklist: 1,
|
|
2374
|
+
aiStoreList: 1,
|
|
2375
|
+
runAIQuestionCount: 1,
|
|
2376
|
+
},
|
|
2377
|
+
} );
|
|
2378
|
+
|
|
2379
|
+
findQuery.push( {
|
|
2380
|
+
$project: {
|
|
2381
|
+
checkListName: 1,
|
|
2382
|
+
checkListChar: 1,
|
|
2383
|
+
coverage: {
|
|
2384
|
+
$concat: [
|
|
2385
|
+
{ $toUpper: { $substr: [ { $ifNull: [ '$coverage', '' ] }, 0, 1 ] } },
|
|
2386
|
+
{ $substr: [ { $ifNull: [ '$coverage', '' ] }, 1, { $strLenCP: { $ifNull: [ '$coverage', '' ] } } ] },
|
|
2387
|
+
],
|
|
2388
|
+
},
|
|
2389
|
+
sourceCheckList_id: 1,
|
|
2390
|
+
checkListType: 1,
|
|
2391
|
+
flagType: 1,
|
|
2392
|
+
assignedStores: 1,
|
|
2393
|
+
assignedStoresAi: 1,
|
|
2394
|
+
flaggedStores: 1,
|
|
2395
|
+
flagCount: 1,
|
|
2396
|
+
uniqueFlaggedStores: 1,
|
|
2397
|
+
complianceRate: {
|
|
2398
|
+
$cond: {
|
|
2399
|
+
if: { $eq: [ '$questionCount', 0 ] },
|
|
2400
|
+
then: 0,
|
|
2401
|
+
else: {
|
|
2402
|
+
$round: [ { $multiply: [ { $divide: [ '$correctAnswers', '$questionCount' ] }, 100 ] }, 2 ],
|
|
2403
|
+
},
|
|
2404
|
+
},
|
|
2405
|
+
},
|
|
2406
|
+
customQuestionFlagCount: 1,
|
|
2407
|
+
customTimeFlagCount: 1,
|
|
2408
|
+
submittedChecklist: 1,
|
|
2409
|
+
aiStoreList: 1,
|
|
2410
|
+
runAIQuestionCount: 1,
|
|
2411
|
+
},
|
|
2412
|
+
} );
|
|
2413
|
+
|
|
2414
|
+
findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
|
|
2415
|
+
return findQuery;
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// Merges detection / runAI / all flag values from the Lambda into the aggregated
|
|
2419
|
+
// rows (mutates in place). Single source of truth shared by flagTablesV2's export
|
|
2420
|
+
// path and flagTablesDetectionV2 — the detection-column values (flaggedStores,
|
|
2421
|
+
// flagCount, complianceRate, assignedStores) and their edge cases live here only.
|
|
2422
|
+
function mergeDetectionIntoRows( getChecklistPerformanceData, lambdaResultData, requestData ) {
|
|
2423
|
+
if ( !getChecklistPerformanceData?.length ) return;
|
|
2424
|
+
const detectionChecklists = getChecklistPerformanceData?.filter( ( val ) => val?.checkListType !== 'custom' );
|
|
2425
|
+
if ( !( detectionChecklists?.length || [ 'runAI', 'all' ].includes( requestData?.filter ) ) ) return;
|
|
2426
|
+
const resultData = lambdaResultData;
|
|
2427
|
+
if ( !resultData || resultData.status_code != '200' ) return;
|
|
2428
|
+
|
|
2429
|
+
for ( let index = 0; index < getChecklistPerformanceData.length; index++ ) {
|
|
2430
|
+
if ( [ 'runAI', 'all' ].includes( requestData?.filter ) ) {
|
|
2431
|
+
let findCheckList = resultData?.runAI?.list?.find( ( ele ) => ele.checkListName == getChecklistPerformanceData[index].checkListName );
|
|
2432
|
+
if ( findCheckList ) {
|
|
2433
|
+
if ( requestData?.filter == 'all' ) {
|
|
2434
|
+
getChecklistPerformanceData[index].runAIFlag = findCheckList.flagCount;
|
|
2435
|
+
getChecklistPerformanceData[index].flaggedStores = getChecklistPerformanceData[index].flaggedStores + findCheckList.flagCount;
|
|
2436
|
+
} else {
|
|
2437
|
+
getChecklistPerformanceData[index].flagCount = findCheckList.flagCount;
|
|
2438
|
+
getChecklistPerformanceData[index].flaggedStores = findCheckList.flagStoreCount;
|
|
2439
|
+
}
|
|
2440
|
+
} else {
|
|
2441
|
+
if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
|
|
2442
|
+
if ( requestData?.filter == 'all' ) {
|
|
2443
|
+
getChecklistPerformanceData[index].runAIFlag = 0;
|
|
2444
|
+
getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
|
|
2445
|
+
getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
|
|
2446
|
+
} else {
|
|
2447
|
+
getChecklistPerformanceData[index].flagCount = 0;
|
|
2448
|
+
getChecklistPerformanceData[index].flaggedStores = 0;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
|
|
2453
|
+
getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
|
|
2454
|
+
getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
|
|
2455
|
+
} else {
|
|
2456
|
+
if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
|
|
2457
|
+
getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
|
|
2458
|
+
getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
|
|
2459
|
+
getChecklistPerformanceData[index].assignedStores = getChecklistPerformanceData[index]?.aiStoreList?.filter( ( element ) => requestData.storeId.includes( element ) )?.length || 0;
|
|
2460
|
+
getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
|
|
2461
|
+
getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
|
|
2462
|
+
getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2085
2468
|
export const flagTablesV2 = async ( req, res ) => {
|
|
2086
2469
|
try {
|
|
2087
2470
|
let requestData = req.body;
|
|
@@ -2091,8 +2474,6 @@ export const flagTablesV2 = async ( req, res ) => {
|
|
|
2091
2474
|
toDate = new Date( toDate.getTime() - userTimezoneOffset );
|
|
2092
2475
|
toDate.setUTCHours( 23, 59, 59, 59 );
|
|
2093
2476
|
let result = {};
|
|
2094
|
-
let limit = parseInt( requestData?.limit ) || 10;
|
|
2095
|
-
let skip = limit * ( requestData?.offset ) || 0;
|
|
2096
2477
|
|
|
2097
2478
|
const lamdaUrl = JSON.parse( process.env.LAMBDAURL );
|
|
2098
2479
|
|
|
@@ -2281,10 +2662,11 @@ export const flagTablesV2 = async ( req, res ) => {
|
|
|
2281
2662
|
// fixes. One row per checklist keeps the in-memory set small.
|
|
2282
2663
|
findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
|
|
2283
2664
|
|
|
2284
|
-
//
|
|
2285
|
-
//
|
|
2665
|
+
// Only the export download needs the detection values merged inline (a one-off
|
|
2666
|
+
// download can tolerate the ~8s Lambda). The interactive table load skips the
|
|
2667
|
+
// Lambda entirely and hydrates detection values via flagTablesDetectionV2.
|
|
2286
2668
|
// 'question'/'time' filters are custom-only, so the Lambda is never needed there.
|
|
2287
|
-
const mayNeedLambda = ![ 'question', 'time' ].includes( requestData?.filter );
|
|
2669
|
+
const mayNeedLambda = !!requestData.export && ![ 'question', 'time' ].includes( requestData?.filter );
|
|
2288
2670
|
const detectionPayload = {
|
|
2289
2671
|
'fromDate': requestData.fromDate,
|
|
2290
2672
|
'toDate': requestData.toDate,
|
|
@@ -2307,54 +2689,10 @@ export const flagTablesV2 = async ( req, res ) => {
|
|
|
2307
2689
|
}
|
|
2308
2690
|
|
|
2309
2691
|
let getTotalCount = getChecklistPerformanceData.length;
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
let resultData = lambdaResultData;
|
|
2315
|
-
if ( resultData ) {
|
|
2316
|
-
if ( resultData.status_code == '200' ) {
|
|
2317
|
-
for ( let index = 0; index < getChecklistPerformanceData.length; index++ ) {
|
|
2318
|
-
if ( [ 'runAI', 'all' ].includes( requestData?.filter ) ) {
|
|
2319
|
-
let findCheckList = resultData?.runAI?.list?.find( ( ele ) => ele.checkListName == getChecklistPerformanceData[index].checkListName );
|
|
2320
|
-
if ( findCheckList ) {
|
|
2321
|
-
if ( requestData?.filter == 'all' ) {
|
|
2322
|
-
getChecklistPerformanceData[index].runAIFlag = findCheckList.flagCount;
|
|
2323
|
-
getChecklistPerformanceData[index].flaggedStores = getChecklistPerformanceData[index].flaggedStores + findCheckList.flagCount;
|
|
2324
|
-
} else {
|
|
2325
|
-
getChecklistPerformanceData[index].flagCount = findCheckList.flagCount;
|
|
2326
|
-
getChecklistPerformanceData[index].flaggedStores = findCheckList.flagStoreCount;
|
|
2327
|
-
}
|
|
2328
|
-
} else {
|
|
2329
|
-
if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
|
|
2330
|
-
if ( requestData?.filter == 'all' ) {
|
|
2331
|
-
getChecklistPerformanceData[index].runAIFlag = 0;
|
|
2332
|
-
getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
|
|
2333
|
-
getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
|
|
2334
|
-
} else {
|
|
2335
|
-
getChecklistPerformanceData[index].flagCount = 0;
|
|
2336
|
-
getChecklistPerformanceData[index].flaggedStores = 0;
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
|
|
2341
|
-
getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
|
|
2342
|
-
getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
|
|
2343
|
-
} else {
|
|
2344
|
-
if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
|
|
2345
|
-
getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
|
|
2346
|
-
getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
|
|
2347
|
-
getChecklistPerformanceData[index].assignedStores = getChecklistPerformanceData[index]?.aiStoreList?.filter( ( element ) => requestData.storeId.includes( element ) )?.length || 0;
|
|
2348
|
-
getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
|
|
2349
|
-
getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
|
|
2350
|
-
getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2692
|
+
// For export, lambdaResultData is populated and detection values are merged in
|
|
2693
|
+
// here. For the interactive load lambdaResultData is null, so this is a no-op
|
|
2694
|
+
// and the detection values are hydrated later via flagTablesDetectionV2.
|
|
2695
|
+
mergeDetectionIntoRows( getChecklistPerformanceData, lambdaResultData, requestData );
|
|
2358
2696
|
|
|
2359
2697
|
// Sort in JS AFTER the Lambda merge so detection / runAI / all rows order by
|
|
2360
2698
|
// their real (merged) flaggedStores / flagCount / complianceRate / assignedStores
|
|
@@ -2446,8 +2784,10 @@ export const flagTablesV2 = async ( req, res ) => {
|
|
|
2446
2784
|
}
|
|
2447
2785
|
|
|
2448
2786
|
result.totalCount = getTotalCount;
|
|
2449
|
-
//
|
|
2450
|
-
|
|
2787
|
+
// Fast path returns the FULL Mongo-only set (one row per checklist); the client
|
|
2788
|
+
// paginates and re-sorts after merging the detection values from
|
|
2789
|
+
// flagTablesDetectionV2. Export returns earlier via download() above.
|
|
2790
|
+
result.checklistPerformance = getChecklistPerformanceData;
|
|
2451
2791
|
return res.sendSuccess( result );
|
|
2452
2792
|
} catch ( error ) {
|
|
2453
2793
|
console.log( 'error =>', error );
|
|
@@ -2456,6 +2796,71 @@ export const flagTablesV2 = async ( req, res ) => {
|
|
|
2456
2796
|
}
|
|
2457
2797
|
};
|
|
2458
2798
|
|
|
2799
|
+
// Detection-only companion for flagTablesV2. Re-runs the SAME (cheap) aggregation
|
|
2800
|
+
// alongside the detection Lambda (the slow part), merges the detection values, and
|
|
2801
|
+
// returns them keyed by sourceCheckList_id. The client patches these onto the rows
|
|
2802
|
+
// it already rendered from flagTablesV2, then re-sorts / re-paginates:
|
|
2803
|
+
// for ( const row of rows ) {
|
|
2804
|
+
// const d = res.detection[ row.sourceCheckList_id ];
|
|
2805
|
+
// if ( d ) Object.assign( row, d );
|
|
2806
|
+
// }
|
|
2807
|
+
// The aggregation is re-run (not passed in) so the two endpoints stay independent
|
|
2808
|
+
// and the Lambda still runs concurrently with Mongo; the aggregation is cheap
|
|
2809
|
+
// relative to the ~8s Lambda that dominates this call's latency.
|
|
2810
|
+
export const flagTablesDetectionV2 = async ( req, res ) => {
|
|
2811
|
+
try {
|
|
2812
|
+
const requestData = req.body;
|
|
2813
|
+
|
|
2814
|
+
// 'question'/'time' filters are custom-only — no detection data to fetch.
|
|
2815
|
+
const mayNeedLambda = ![ 'question', 'time' ].includes( requestData?.filter );
|
|
2816
|
+
if ( !mayNeedLambda ) {
|
|
2817
|
+
return res.sendSuccess( { detection: {} } );
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
const findQuery = buildFlagTablesFindQuery( requestData );
|
|
2821
|
+
|
|
2822
|
+
const lamdaUrl = JSON.parse( process.env.LAMBDAURL );
|
|
2823
|
+
const LamdaURL = lamdaUrl.flagTablesV2;
|
|
2824
|
+
const detectionPayload = {
|
|
2825
|
+
'fromDate': requestData.fromDate,
|
|
2826
|
+
'toDate': requestData.toDate,
|
|
2827
|
+
'storeId': requestData.storeId,
|
|
2828
|
+
'clientId': requestData.clientId,
|
|
2829
|
+
};
|
|
2830
|
+
|
|
2831
|
+
const [ getChecklistPerformanceData, lambdaResultData ] = await Promise.all( [
|
|
2832
|
+
processedchecklistService.aggregate( findQuery ),
|
|
2833
|
+
LamdaServiceCall( LamdaURL, detectionPayload ),
|
|
2834
|
+
] );
|
|
2835
|
+
|
|
2836
|
+
if ( !getChecklistPerformanceData?.length ) {
|
|
2837
|
+
return res.sendSuccess( { detection: {} } );
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
mergeDetectionIntoRows( getChecklistPerformanceData, lambdaResultData, requestData );
|
|
2841
|
+
|
|
2842
|
+
// Return only the detection-affected fields, keyed by sourceCheckList_id, so the
|
|
2843
|
+
// client can patch them onto its already-rendered rows.
|
|
2844
|
+
const detection = {};
|
|
2845
|
+
getChecklistPerformanceData.forEach( ( row ) => {
|
|
2846
|
+
const key = String( row.sourceCheckList_id ?? row._id );
|
|
2847
|
+
detection[key] = {
|
|
2848
|
+
flagCount: row.flagCount,
|
|
2849
|
+
flaggedStores: row.flaggedStores,
|
|
2850
|
+
complianceRate: row.complianceRate,
|
|
2851
|
+
assignedStores: row.assignedStores,
|
|
2852
|
+
...( row.runAIFlag !== undefined ? { runAIFlag: row.runAIFlag } : {} ),
|
|
2853
|
+
};
|
|
2854
|
+
} );
|
|
2855
|
+
|
|
2856
|
+
return res.sendSuccess( { detection } );
|
|
2857
|
+
} catch ( error ) {
|
|
2858
|
+
console.log( 'error =>', error );
|
|
2859
|
+
logger.error( { error: error, message: req.query, function: 'flagTablesDetectionV2' } );
|
|
2860
|
+
return res.sendError( { error: error }, 500 );
|
|
2861
|
+
}
|
|
2862
|
+
};
|
|
2863
|
+
|
|
2459
2864
|
export const checklistCountByType = async ( req, res ) => {
|
|
2460
2865
|
try {
|
|
2461
2866
|
let { clientId, fromDate, toDate } = req.body;
|
|
@@ -9,11 +9,14 @@ import {
|
|
|
9
9
|
flagChecklistTableV1,
|
|
10
10
|
overallFlagMetrics,
|
|
11
11
|
flagCardsV1,
|
|
12
|
+
flagCardsDetectionV1,
|
|
12
13
|
flagComparisonCardsV1,
|
|
13
14
|
flagTablesV1,
|
|
14
15
|
checklistDropdownV1,
|
|
15
16
|
flagTablesV2,
|
|
17
|
+
flagTablesDetectionV2,
|
|
16
18
|
flagComparisonCardsV2,
|
|
19
|
+
flagComparisonCardsDetectionV2,
|
|
17
20
|
checklistCountByType,
|
|
18
21
|
} from '../controllers/teaxFlag.controller.js';
|
|
19
22
|
import { isAllowedSessionHandler } from 'tango-app-api-middleware';
|
|
@@ -42,6 +45,9 @@ traxFlagRouter
|
|
|
42
45
|
.post( '/flagCardsV1', isAllowedSessionHandler,
|
|
43
46
|
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
44
47
|
flagCardsV1 )
|
|
48
|
+
.post( '/flagCardsDetectionV1', isAllowedSessionHandler,
|
|
49
|
+
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
50
|
+
flagCardsDetectionV1 )
|
|
45
51
|
.post( '/flagComparisonCardsV1', isAllowedSessionHandler,
|
|
46
52
|
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
47
53
|
flagComparisonCardsV1 )
|
|
@@ -51,9 +57,15 @@ traxFlagRouter
|
|
|
51
57
|
.post( '/flagComparisonCardsV2', isAllowedSessionHandler,
|
|
52
58
|
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
53
59
|
flagComparisonCardsV2 )
|
|
60
|
+
.post( '/flagComparisonCardsDetectionV2', isAllowedSessionHandler,
|
|
61
|
+
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
62
|
+
flagComparisonCardsDetectionV2 )
|
|
54
63
|
.post( '/flagTablesV2', isAllowedSessionHandler,
|
|
55
64
|
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
56
65
|
flagTablesV2 )
|
|
66
|
+
.post( '/flagTablesDetectionV2', isAllowedSessionHandler,
|
|
67
|
+
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
68
|
+
flagTablesDetectionV2 )
|
|
57
69
|
.post( '/checklistCountByType', isAllowedSessionHandler,
|
|
58
70
|
accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
|
|
59
71
|
checklistCountByType );
|