tango-app-api-trax 3.9.68 → 3.9.70

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.68",
3
+ "version": "3.9.70",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -69,12 +69,45 @@ export async function viewchecklist( req, res ) {
69
69
  try {
70
70
  let url = JSON.parse( process.env.LAMBDAURL );
71
71
  let resultData = await LamdaServiceCall( url.checklistAnswer, req.body );
72
- if ( resultData ) {
73
- if ( resultData.status_code == '200' ) {
74
- return res.sendSuccess( resultData );
75
- } else {
76
- return res.sendError( 'No Content', 204 );
72
+ // Guard before touching resultData LamdaServiceCall returns false on failure.
73
+ if ( !resultData ) {
74
+ return res.sendError( 'No Content', 204 );
75
+ }
76
+
77
+ const answers = resultData.checklistAnswers || [];
78
+ // Enrich each answer with its store's SPOC email. Batch every store lookup
79
+ // into a single $in query (one round-trip instead of one per answer), indexed
80
+ // by storeId for an in-memory lookup below.
81
+ const storeIds = [ ...new Set(
82
+ answers
83
+ .filter( ( ele ) => ele?.storeProfile?.storeName && ele?.storeProfile?.store_id )
84
+ .map( ( ele ) => ele?.storeProfile?.store_id ),
85
+ ) ];
86
+ const spocEmailByStoreId = {};
87
+ if ( storeIds.length ) {
88
+ const storeDetails = await storeService.find(
89
+ { storeId: { $in: storeIds } },
90
+ { storeId: 1, spocDetails: 1 },
91
+ );
92
+ storeDetails?.forEach( ( store ) => {
93
+ if ( store.spocDetails?.[0]?.email ) {
94
+ spocEmailByStoreId[store.storeId] = { email: store.spocDetails?.[0]?.email, name: store.spocDetails?.[0]?.name };
95
+ }
96
+ } );
97
+ }
98
+ // Keep every answer; only attach the SPOC email when we found one.
99
+ resultData.checklistAnswers = answers.map( ( ele ) => {
100
+ ele.storeProfile.userList = [ { name: ele.storeProfile.userName, email: ele.storeProfile.userEmail } ];
101
+ if ( ele?.storeProfile?.storeName && spocEmailByStoreId[ele?.storeProfile?.store_id] ) {
102
+ if ( !ele.storeProfile.userList.find( ( list ) => list.email == spocEmailByStoreId[ele?.storeProfile?.store_id]?.email ) ) {
103
+ ele.storeProfile.userList.push( { name: spocEmailByStoreId[ele?.storeProfile?.store_id]?.name, email: spocEmailByStoreId[ele?.storeProfile?.store_id]?.email } );
104
+ }
77
105
  }
106
+ return ele;
107
+ } );
108
+
109
+ if ( resultData.status_code == '200' ) {
110
+ return res.sendSuccess( resultData );
78
111
  } else {
79
112
  return res.sendError( 'No Content', 204 );
80
113
  }
@@ -451,7 +484,7 @@ export async function redoChecklist( req, res ) {
451
484
 
452
485
  let findQuestion = question[sectionIndex].questions.findIndex( ( ele ) => ele.qno == req.body.payload.qno );
453
486
 
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 || '' } ) };
487
+ let data = { ...question[sectionIndex].questions[findQuestion], redo: true, ...( !req.body.payload?.answerIndex && { redoComment: req.body.payload?.checklistDescription || '' } ) };
455
488
  let answerIndex = req.body.payload?.answerIndex;
456
489
  let isSingleAnswerRedo = answerIndex != null;
457
490
 
@@ -575,7 +608,7 @@ export async function redoChecklist( req, res ) {
575
608
  type: checklistDetails.checkListType,
576
609
  userAnswer: userAnswer,
577
610
  initiatedBy: req.user.userName,
578
- initiatedTime: dayjs().format(),
611
+ initiatedTime: dayjs.utc( ).format(),
579
612
  answerType: question[sectionIndex].questions[findQuestion].answerType,
580
613
  submitedBy: checklistDetails.userName,
581
614
  submitTime: dayjs.utc( checklistDetails.submitTime ).format(),
@@ -667,7 +700,7 @@ export async function redomultiChecklist( req, res ) {
667
700
 
668
701
  let findQuestion = question[sectionIndex].questions.findIndex( ( ele ) => ele.qno == req.body.payload.qno );
669
702
 
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 };
703
+ let data = { ...question[sectionIndex].questions[findQuestion], redo: true, redoComment: req.body.payload?.showcomment ? req.body.payload?.checklistDescription : findcomment?.Comments };
671
704
  // if ( checklistDetails.client_id == '458' ) {
672
705
  data.answers.forEach( ( item ) => {
673
706
  if ( item.showLinked ) {
@@ -2669,12 +2669,7 @@ function QuestionFlag( req, res ) {
2669
2669
  let answervalidate = question.userAnswer.filter( ( answer ) => {
2670
2670
  return answer.sopFlag == true;
2671
2671
  } );
2672
- let answerCount = 0;
2673
- if ( answervalidate?.length ) {
2674
- answerCount = 1;
2675
- }
2676
- // flagCount = flagCount + answervalidate.length;
2677
- flagCount = flagCount + answerCount;
2672
+ flagCount = flagCount + answervalidate.length;
2678
2673
  }
2679
2674
  } );
2680
2675
  } );
@@ -4221,6 +4216,7 @@ export async function questionList( req, res ) {
4221
4216
  findQuery.push( {
4222
4217
  $project: {
4223
4218
  checkListName: { $ifNull: [ '$checkListName', '' ] },
4219
+ checkListDescription: { $ifNull: [ '$checkListDescription', '' ] },
4224
4220
  scheduleStartTime: { $ifNull: [ '$scheduleStartTime', '' ] },
4225
4221
  scheduleStartTime_iso: { $ifNull: [ '$scheduleStartTime_iso', '' ] },
4226
4222
  scheduleEndTime: { $ifNull: [ '$scheduleEndTime', '' ] },
@@ -4604,6 +4600,7 @@ export async function taskQuestionList( req, res ) {
4604
4600
  findQuery.push( {
4605
4601
  $project: {
4606
4602
  checkListName: { $ifNull: [ '$checkListName', '' ] },
4603
+ checkListDescription: { $ifNull: [ '$checkListDescription', '' ] },
4607
4604
  scheduleStartTime: { $ifNull: [ '$scheduleStartTime', '' ] },
4608
4605
  scheduleStartTime_iso: { $ifNull: [ '$scheduleStartTime_iso', '' ] },
4609
4606
  scheduleEndTime: { $ifNull: [ '$scheduleEndTime', '' ] },
@@ -5249,6 +5246,7 @@ export async function questionListV1( req, res ) {
5249
5246
  findQuery.push( {
5250
5247
  $project: {
5251
5248
  checkListName: { $ifNull: [ '$checkListName', '' ] },
5249
+ checkListDescription: { $ifNull: [ '$checkListDescription', '' ] },
5252
5250
  scheduleStartTime: { $ifNull: [ '$scheduleStartTime', '' ] },
5253
5251
  scheduleStartTime_iso: { $ifNull: [ '$scheduleStartTime_iso', '' ] },
5254
5252
  scheduleEndTime: { $ifNull: [ '$scheduleEndTime', '' ] },
@@ -444,47 +444,13 @@ export const flagCardsV1 = async ( req, res ) => {
444
444
  },
445
445
  ];
446
446
 
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
-
447
+ // These three lookups are independent of one another, so run them concurrently
448
+ // instead of sequentially (Mongo aggregation + config find + detection Lambda).
484
449
  const detectionPayload = { fromDate, toDate, storeId, clientId };
485
450
  const lambdaURL = 'https://f65azvtljclaxp6l7rnx65cdmm0lcgvp.lambda-url.ap-south-1.on.aws/';
486
451
 
487
- const [ publishedAiChecklists, resultData ] = await Promise.all( [
452
+ const [ getOverallChecklistData, publishedAiChecklists, resultData ] = await Promise.all( [
453
+ processedchecklistService.aggregate( findQuery ),
488
454
  checklistconfigService.find(
489
455
  {
490
456
  client_id: clientId,
@@ -496,6 +462,15 @@ export const flagCardsDetectionV1 = async ( req, res ) => {
496
462
  LamdaServiceCall( lambdaURL, detectionPayload ),
497
463
  ] );
498
464
 
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
+
499
474
  const published = publishedAiChecklists.map( ( val ) => val?.checkListType );
500
475
  if ( resultData && resultData.status_code === '200' ) {
501
476
  published.forEach( ( item ) => {
@@ -503,18 +478,18 @@ export const flagCardsDetectionV1 = async ( req, res ) => {
503
478
  flagCards.detectionFlag.count += resultData[item];
504
479
  }
505
480
  } );
506
- flagCards.detectionTotalFlag += flagCards.detectionFlag.count;
481
+ flagCards.totalFlag += flagCards.detectionFlag.count;
507
482
  if ( resultData?.runAI ) {
508
483
  resultData.runAI.flagCount = resultData.runaiflags;
509
484
  flagCards.runAIFlag = resultData?.runAI;
510
- flagCards.detectionTotalFlag += resultData.runaiflags;
485
+ flagCards.totalFlag += resultData.runaiflags;
511
486
  }
512
487
  }
513
488
 
514
489
  return res.sendSuccess( { flagCards } );
515
490
  } catch ( error ) {
516
- console.error( 'Error in flagCardsDetectionV1:', error );
517
- logger.error( { error, function: 'flagCardsDetectionV1' } );
491
+ console.error( 'Error in flagCardsV1:', error );
492
+ logger.error( { error, function: 'flagCardsV1' } );
518
493
  return res.sendError( error, 500 );
519
494
  }
520
495
  };
@@ -2021,151 +1996,6 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
2021
1996
  return { comparisonData, ComparisonFlag };
2022
1997
  };
2023
1998
 
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
-
2169
1999
  const getDetectionCount = async ( fromDate, toDate ) => {
2170
2000
  const detectionPayload = {
2171
2001
  fromDate: dayjs( fromDate ).format( 'YYYY-MM-DD' ),
@@ -2174,6 +2004,7 @@ export const flagComparisonCardsDetectionV2 = async ( req, res ) => {
2174
2004
  clientId: requestData.clientId,
2175
2005
  };
2176
2006
 
2007
+ // The config lookup and the detection Lambda are independent — run them together.
2177
2008
  const LamdaURL = 'https://f65azvtljclaxp6l7rnx65cdmm0lcgvp.lambda-url.ap-south-1.on.aws/';
2178
2009
  const [ publishedAiChecklists, resultData ] = await Promise.all( [
2179
2010
  checklistconfigService.find(
@@ -2186,6 +2017,9 @@ export const flagComparisonCardsDetectionV2 = async ( req, res ) => {
2186
2017
  ),
2187
2018
  LamdaServiceCall( LamdaURL, detectionPayload ),
2188
2019
  ] );
2020
+ // if ( !publishedAiChecklists?.length ) {
2021
+ // return 0;
2022
+ // }
2189
2023
  const published = publishedAiChecklists.map( ( val ) => val?.checkListType );
2190
2024
  if ( resultData?.status_code === '200' ) {
2191
2025
  let result = 0;
@@ -2203,268 +2037,51 @@ export const flagComparisonCardsDetectionV2 = async ( req, res ) => {
2203
2037
  return 0;
2204
2038
  };
2205
2039
 
2206
- const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount ] = await Promise.all( [
2040
+ const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount, traxMonthlyComparisonData ] = await Promise.all( [
2207
2041
  processedchecklistService.aggregate( createFindQuery( rangeOneFromDate, rangeOneToDate ) ),
2208
2042
  processedchecklistService.aggregate( createFindQuery( rangeTwoFromDate, rangeTwoToDate ) ),
2209
2043
  getDetectionCount( rangeOneFromDate, rangeOneToDate ),
2210
2044
  getDetectionCount( rangeTwoFromDate, rangeTwoToDate ),
2045
+ clientService.findOne( { clientId: requestData.clientId }, { traxMonthlyComparison: 1 } ),
2211
2046
  ] );
2212
2047
 
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
+
2213
2056
  const flagComparisonCards = {
2214
2057
  totalComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2058
+ questionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2059
+ delayInSubmissionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2215
2060
  detectionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2216
2061
  runAIComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2217
2062
  };
2218
2063
 
2219
2064
  if ( rangeOneData.length && rangeTwoData.length ) {
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;
2065
+ let rangeOneTotalFlag = rangeOneData[0].totalFlag + rangeOneDetectionCount.result || 0;
2066
+ let rangeTwoTotalFlag = rangeTwoData[0].totalFlag + rangeTwoDetectionCount.result || 0;
2228
2067
  flagComparisonCards.totalComparisonFlag = calculateComparison( rangeOneTotalFlag, rangeTwoTotalFlag );
2229
- flagComparisonCards.detectionComparisonFlag = calculateComparison( rangeOneDetection, rangeTwoDetection );
2230
- flagComparisonCards.runAIComparisonFlag = calculateComparison( rangeOneAi, rangeTwoAi );
2068
+ flagComparisonCards.questionComparisonFlag = calculateComparison( rangeOneData[0].questionFlag, rangeTwoData[0].questionFlag );
2069
+ flagComparisonCards.delayInSubmissionComparisonFlag = calculateComparison( rangeOneData[0].delayInSubmission, rangeTwoData[0].delayInSubmission );
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;
2231
2076
  }
2232
-
2233
2077
  return res.sendSuccess( { flagComparisonCards } );
2234
2078
  } catch ( error ) {
2235
2079
  console.log( 'error =>', error );
2236
- logger.error( { error, function: 'flagComparisonCardsDetectionV2' } );
2080
+ logger.error( { error, function: 'flagComparisonCardsV1' } );
2237
2081
  return res.sendError( error, 500 );
2238
2082
  }
2239
2083
  };
2240
2084
 
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
-
2468
2085
  export const flagTablesV2 = async ( req, res ) => {
2469
2086
  try {
2470
2087
  let requestData = req.body;
@@ -2474,6 +2091,8 @@ export const flagTablesV2 = async ( req, res ) => {
2474
2091
  toDate = new Date( toDate.getTime() - userTimezoneOffset );
2475
2092
  toDate.setUTCHours( 23, 59, 59, 59 );
2476
2093
  let result = {};
2094
+ let limit = parseInt( requestData?.limit ) || 10;
2095
+ let skip = limit * ( requestData?.offset ) || 0;
2477
2096
 
2478
2097
  const lamdaUrl = JSON.parse( process.env.LAMBDAURL );
2479
2098
 
@@ -2662,11 +2281,10 @@ export const flagTablesV2 = async ( req, res ) => {
2662
2281
  // fixes. One row per checklist keeps the in-memory set small.
2663
2282
  findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
2664
2283
 
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.
2284
+ // The detection Lambda only needs request params (not the aggregation result),
2285
+ // so fire it concurrently with the Mongo aggregation instead of after it.
2668
2286
  // 'question'/'time' filters are custom-only, so the Lambda is never needed there.
2669
- const mayNeedLambda = !!requestData.export && ![ 'question', 'time' ].includes( requestData?.filter );
2287
+ const mayNeedLambda = ![ 'question', 'time' ].includes( requestData?.filter );
2670
2288
  const detectionPayload = {
2671
2289
  'fromDate': requestData.fromDate,
2672
2290
  'toDate': requestData.toDate,
@@ -2689,10 +2307,54 @@ export const flagTablesV2 = async ( req, res ) => {
2689
2307
  }
2690
2308
 
2691
2309
  let getTotalCount = getChecklistPerformanceData.length;
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 );
2310
+ if ( getChecklistPerformanceData.length ) {
2311
+ const detectionChecklists = getChecklistPerformanceData?.filter( ( val ) => val?.checkListType !== 'custom' );
2312
+
2313
+ if ( detectionChecklists?.length || [ 'runAI', 'all' ].includes( requestData?.filter ) ) {
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
+ }
2696
2358
 
2697
2359
  // Sort in JS AFTER the Lambda merge so detection / runAI / all rows order by
2698
2360
  // their real (merged) flaggedStores / flagCount / complianceRate / assignedStores
@@ -2784,10 +2446,8 @@ export const flagTablesV2 = async ( req, res ) => {
2784
2446
  }
2785
2447
 
2786
2448
  result.totalCount = getTotalCount;
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;
2449
+ // Paginate in JS now that the full set is sorted by the merged values.
2450
+ result.checklistPerformance = getChecklistPerformanceData.slice( skip, skip + limit );
2791
2451
  return res.sendSuccess( result );
2792
2452
  } catch ( error ) {
2793
2453
  console.log( 'error =>', error );
@@ -2796,71 +2456,6 @@ export const flagTablesV2 = async ( req, res ) => {
2796
2456
  }
2797
2457
  };
2798
2458
 
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
-
2864
2459
  export const checklistCountByType = async ( req, res ) => {
2865
2460
  try {
2866
2461
  let { clientId, fromDate, toDate } = req.body;
@@ -9,14 +9,11 @@ import {
9
9
  flagChecklistTableV1,
10
10
  overallFlagMetrics,
11
11
  flagCardsV1,
12
- flagCardsDetectionV1,
13
12
  flagComparisonCardsV1,
14
13
  flagTablesV1,
15
14
  checklistDropdownV1,
16
15
  flagTablesV2,
17
- flagTablesDetectionV2,
18
16
  flagComparisonCardsV2,
19
- flagComparisonCardsDetectionV2,
20
17
  checklistCountByType,
21
18
  } from '../controllers/teaxFlag.controller.js';
22
19
  import { isAllowedSessionHandler } from 'tango-app-api-middleware';
@@ -45,9 +42,6 @@ traxFlagRouter
45
42
  .post( '/flagCardsV1', isAllowedSessionHandler,
46
43
  accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
47
44
  flagCardsV1 )
48
- .post( '/flagCardsDetectionV1', isAllowedSessionHandler,
49
- accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
50
- flagCardsDetectionV1 )
51
45
  .post( '/flagComparisonCardsV1', isAllowedSessionHandler,
52
46
  accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
53
47
  flagComparisonCardsV1 )
@@ -57,15 +51,9 @@ traxFlagRouter
57
51
  .post( '/flagComparisonCardsV2', isAllowedSessionHandler,
58
52
  accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
59
53
  flagComparisonCardsV2 )
60
- .post( '/flagComparisonCardsDetectionV2', isAllowedSessionHandler,
61
- accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
62
- flagComparisonCardsDetectionV2 )
63
54
  .post( '/flagTablesV2', isAllowedSessionHandler,
64
55
  accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
65
56
  flagTablesV2 )
66
- .post( '/flagTablesDetectionV2', isAllowedSessionHandler,
67
- accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
68
- flagTablesDetectionV2 )
69
57
  .post( '/checklistCountByType', isAllowedSessionHandler,
70
58
  accessVerification( { userType: [ 'tango', 'client' ], access: [ { featureName: 'TangoTrax', name: 'checklist', permissions: [] } ] } ),
71
59
  checklistCountByType );