tango-app-api-trax 3.9.66 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.66",
3
+ "version": "3.9.68",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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.utc( ).format(),
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
- flagCount = flagCount + answervalidate.length;
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
- // These three lookups are independent of one another, so run them concurrently
448
- // instead of sequentially (Mongo aggregation + config find + detection Lambda).
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 [ getOverallChecklistData, publishedAiChecklists, resultData ] = await Promise.all( [
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.totalFlag += flagCards.detectionFlag.count;
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.totalFlag += resultData.runaiflags;
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 flagCardsV1:', error );
492
- logger.error( { error, function: 'flagCardsV1' } );
516
+ console.error( 'Error in flagCardsDetectionV1:', error );
517
+ logger.error( { error, function: 'flagCardsDetectionV1' } );
493
518
  return res.sendError( error, 500 );
494
519
  }
495
520
  };
@@ -925,8 +950,6 @@ export const flagTablesV1 = async ( req, res ) => {
925
950
 
926
951
  // Lamda Service Call //
927
952
  async function LamdaServiceCall( url, data, timeoutMs = 8000 ) {
928
- const controller = new AbortController();
929
- const timer = setTimeout( () => controller.abort(), timeoutMs );
930
953
  try {
931
954
  const requestOptions = {
932
955
  method: 'POST',
@@ -934,8 +957,6 @@ async function LamdaServiceCall( url, data, timeoutMs = 8000 ) {
934
957
  'Content-Type': 'application/json',
935
958
  },
936
959
  body: JSON.stringify( data ),
937
- keepalive: true,
938
- signal: controller.signal,
939
960
  };
940
961
  const response = await fetch( url, requestOptions );
941
962
  if ( !response.ok ) {
@@ -946,8 +967,6 @@ async function LamdaServiceCall( url, data, timeoutMs = 8000 ) {
946
967
  } catch ( error ) {
947
968
  logger.error( { error: error, message: data, function: 'LamdaServiceCall' } );
948
969
  return false;
949
- } finally {
950
- clearTimeout( timer );
951
970
  }
952
971
  }
953
972
 
@@ -2002,6 +2021,151 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
2002
2021
  return { comparisonData, ComparisonFlag };
2003
2022
  };
2004
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
+
2005
2169
  const getDetectionCount = async ( fromDate, toDate ) => {
2006
2170
  const detectionPayload = {
2007
2171
  fromDate: dayjs( fromDate ).format( 'YYYY-MM-DD' ),
@@ -2010,7 +2174,6 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
2010
2174
  clientId: requestData.clientId,
2011
2175
  };
2012
2176
 
2013
- // The config lookup and the detection Lambda are independent — run them together.
2014
2177
  const LamdaURL = 'https://f65azvtljclaxp6l7rnx65cdmm0lcgvp.lambda-url.ap-south-1.on.aws/';
2015
2178
  const [ publishedAiChecklists, resultData ] = await Promise.all( [
2016
2179
  checklistconfigService.find(
@@ -2023,9 +2186,6 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
2023
2186
  ),
2024
2187
  LamdaServiceCall( LamdaURL, detectionPayload ),
2025
2188
  ] );
2026
- // if ( !publishedAiChecklists?.length ) {
2027
- // return 0;
2028
- // }
2029
2189
  const published = publishedAiChecklists.map( ( val ) => val?.checkListType );
2030
2190
  if ( resultData?.status_code === '200' ) {
2031
2191
  let result = 0;
@@ -2043,51 +2203,268 @@ export const flagComparisonCardsV2 = async ( req, res ) => {
2043
2203
  return 0;
2044
2204
  };
2045
2205
 
2046
- const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount, traxMonthlyComparisonData ] = await Promise.all( [
2206
+ const [ rangeOneData, rangeTwoData, rangeOneDetectionCount, rangeTwoDetectionCount ] = await Promise.all( [
2047
2207
  processedchecklistService.aggregate( createFindQuery( rangeOneFromDate, rangeOneToDate ) ),
2048
2208
  processedchecklistService.aggregate( createFindQuery( rangeTwoFromDate, rangeTwoToDate ) ),
2049
2209
  getDetectionCount( rangeOneFromDate, rangeOneToDate ),
2050
2210
  getDetectionCount( rangeTwoFromDate, rangeTwoToDate ),
2051
- clientService.findOne( { clientId: requestData.clientId }, { traxMonthlyComparison: 1 } ),
2052
2211
  ] );
2053
2212
 
2054
- // console.log( 'rangeOneFromDate =>', rangeOneFromDate );
2055
- // console.log( 'rangeOneToDate =>', rangeOneToDate );
2056
- // console.log( 'rangeOneData =>', rangeOneData );
2057
-
2058
- // console.log( 'rangeTwoFromDate =>', rangeTwoFromDate );
2059
- // console.log( 'rangeTwoToDate =>', rangeTwoToDate );
2060
- // console.log( 'rangeTwoData =>', rangeTwoData );
2061
-
2062
2213
  const flagComparisonCards = {
2063
2214
  totalComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2064
- questionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2065
- delayInSubmissionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2066
2215
  detectionComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2067
2216
  runAIComparisonFlag: { comparisonData: 0, ComparisonFlag: false },
2068
2217
  };
2069
2218
 
2070
2219
  if ( rangeOneData.length && rangeTwoData.length ) {
2071
- let rangeOneTotalFlag = rangeOneData[0].totalFlag + rangeOneDetectionCount.result || 0;
2072
- let rangeTwoTotalFlag = rangeTwoData[0].totalFlag + rangeTwoDetectionCount.result || 0;
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;
2073
2228
  flagComparisonCards.totalComparisonFlag = calculateComparison( rangeOneTotalFlag, rangeTwoTotalFlag );
2074
- flagComparisonCards.questionComparisonFlag = calculateComparison( rangeOneData[0].questionFlag, rangeTwoData[0].questionFlag );
2075
- flagComparisonCards.delayInSubmissionComparisonFlag = calculateComparison( rangeOneData[0].delayInSubmission, rangeTwoData[0].delayInSubmission );
2076
- flagComparisonCards.detectionComparisonFlag = calculateComparison( rangeOneDetectionCount.result, rangeTwoDetectionCount.result );
2077
- flagComparisonCards.runAIComparisonFlag = calculateComparison( rangeOneDetectionCount.ai, rangeTwoDetectionCount.ai );
2078
- }
2079
- flagComparisonCards.traxMonthlyComparison = false;
2080
- if ( traxMonthlyComparisonData && traxMonthlyComparisonData.traxMonthlyComparison ) {
2081
- flagComparisonCards.traxMonthlyComparison = traxMonthlyComparisonData.traxMonthlyComparison;
2229
+ flagComparisonCards.detectionComparisonFlag = calculateComparison( rangeOneDetection, rangeTwoDetection );
2230
+ flagComparisonCards.runAIComparisonFlag = calculateComparison( rangeOneAi, rangeTwoAi );
2082
2231
  }
2232
+
2083
2233
  return res.sendSuccess( { flagComparisonCards } );
2084
2234
  } catch ( error ) {
2085
2235
  console.log( 'error =>', error );
2086
- logger.error( { error, function: 'flagComparisonCardsV1' } );
2236
+ logger.error( { error, function: 'flagComparisonCardsDetectionV2' } );
2087
2237
  return res.sendError( error, 500 );
2088
2238
  }
2089
2239
  };
2090
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
+
2091
2468
  export const flagTablesV2 = async ( req, res ) => {
2092
2469
  try {
2093
2470
  let requestData = req.body;
@@ -2097,8 +2474,6 @@ export const flagTablesV2 = async ( req, res ) => {
2097
2474
  toDate = new Date( toDate.getTime() - userTimezoneOffset );
2098
2475
  toDate.setUTCHours( 23, 59, 59, 59 );
2099
2476
  let result = {};
2100
- let limit = parseInt( requestData?.limit ) || 10;
2101
- let skip = limit * ( requestData?.offset ) || 0;
2102
2477
 
2103
2478
  const lamdaUrl = JSON.parse( process.env.LAMBDAURL );
2104
2479
 
@@ -2287,10 +2662,11 @@ export const flagTablesV2 = async ( req, res ) => {
2287
2662
  // fixes. One row per checklist keeps the in-memory set small.
2288
2663
  findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
2289
2664
 
2290
- // The detection Lambda only needs request params (not the aggregation result),
2291
- // so fire it concurrently with the Mongo aggregation instead of after it.
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.
2292
2668
  // 'question'/'time' filters are custom-only, so the Lambda is never needed there.
2293
- const mayNeedLambda = ![ 'question', 'time' ].includes( requestData?.filter );
2669
+ const mayNeedLambda = !!requestData.export && ![ 'question', 'time' ].includes( requestData?.filter );
2294
2670
  const detectionPayload = {
2295
2671
  'fromDate': requestData.fromDate,
2296
2672
  'toDate': requestData.toDate,
@@ -2306,59 +2682,17 @@ export const flagTablesV2 = async ( req, res ) => {
2306
2682
  mayNeedLambda ? LamdaServiceCall( LamdaURL, detectionPayload ) : Promise.resolve( null ),
2307
2683
  ] );
2308
2684
 
2685
+ console.log( lambdaResultData );
2686
+
2309
2687
  if ( !getChecklistPerformanceData?.length ) {
2310
2688
  return res.sendError( 'No data found', 204 );
2311
2689
  }
2312
2690
 
2313
2691
  let getTotalCount = getChecklistPerformanceData.length;
2314
- if ( getChecklistPerformanceData.length ) {
2315
- const detectionChecklists = getChecklistPerformanceData?.filter( ( val ) => val?.checkListType !== 'custom' );
2316
-
2317
- if ( detectionChecklists?.length || [ 'runAI', 'all' ].includes( requestData?.filter ) ) {
2318
- let resultData = lambdaResultData;
2319
- if ( resultData ) {
2320
- if ( resultData.status_code == '200' ) {
2321
- for ( let index = 0; index < getChecklistPerformanceData.length; index++ ) {
2322
- if ( [ 'runAI', 'all' ].includes( requestData?.filter ) ) {
2323
- let findCheckList = resultData?.runAI?.list?.find( ( ele ) => ele.checkListName == getChecklistPerformanceData[index].checkListName );
2324
- if ( findCheckList ) {
2325
- if ( requestData?.filter == 'all' ) {
2326
- getChecklistPerformanceData[index].runAIFlag = findCheckList.flagCount;
2327
- getChecklistPerformanceData[index].flaggedStores = getChecklistPerformanceData[index].flaggedStores + findCheckList.flagCount;
2328
- } else {
2329
- getChecklistPerformanceData[index].flagCount = findCheckList.flagCount;
2330
- getChecklistPerformanceData[index].flaggedStores = findCheckList.flagStoreCount;
2331
- }
2332
- } else {
2333
- if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
2334
- if ( requestData?.filter == 'all' ) {
2335
- getChecklistPerformanceData[index].runAIFlag = 0;
2336
- getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
2337
- getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
2338
- } else {
2339
- getChecklistPerformanceData[index].flagCount = 0;
2340
- getChecklistPerformanceData[index].flaggedStores = 0;
2341
- }
2342
- }
2343
- }
2344
- getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
2345
- getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
2346
- getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
2347
- } else {
2348
- if ( getChecklistPerformanceData[index].checkListType !== 'custom' ) {
2349
- getChecklistPerformanceData[index].flagCount = resultData?.[getChecklistPerformanceData[index]?.checkListType] || 0;
2350
- getChecklistPerformanceData[index].flaggedStores = resultData?.[`${getChecklistPerformanceData[index]?.checkListType}_flaggedstores`] || 0;
2351
- getChecklistPerformanceData[index].assignedStores = getChecklistPerformanceData[index]?.aiStoreList?.filter( ( element ) => requestData.storeId.includes( element ) )?.length || 0;
2352
- getChecklistPerformanceData[index].complianceRate = ( 100- ( getChecklistPerformanceData[index].flaggedStores / getChecklistPerformanceData[index].assignedStores ) * 100 );
2353
- getChecklistPerformanceData[index].complianceRate = Math.min( 100, Math.max( 0, getChecklistPerformanceData[index].complianceRate ) );
2354
- getChecklistPerformanceData[index].complianceRate = ( getChecklistPerformanceData[index].complianceRate % 1 === 0 ) ? getChecklistPerformanceData[index].complianceRate.toFixed( 0 ) : getChecklistPerformanceData[index].complianceRate.toFixed( 2 );
2355
- }
2356
- }
2357
- }
2358
- }
2359
- }
2360
- }
2361
- }
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 );
2362
2696
 
2363
2697
  // Sort in JS AFTER the Lambda merge so detection / runAI / all rows order by
2364
2698
  // their real (merged) flaggedStores / flagCount / complianceRate / assignedStores
@@ -2450,8 +2784,10 @@ export const flagTablesV2 = async ( req, res ) => {
2450
2784
  }
2451
2785
 
2452
2786
  result.totalCount = getTotalCount;
2453
- // Paginate in JS now that the full set is sorted by the merged values.
2454
- result.checklistPerformance = getChecklistPerformanceData.slice( skip, skip + limit );
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;
2455
2791
  return res.sendSuccess( result );
2456
2792
  } catch ( error ) {
2457
2793
  console.log( 'error =>', error );
@@ -2460,6 +2796,71 @@ export const flagTablesV2 = async ( req, res ) => {
2460
2796
  }
2461
2797
  };
2462
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
+
2463
2864
  export const checklistCountByType = async ( req, res ) => {
2464
2865
  try {
2465
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 );