tango-app-api-trax 3.9.22 → 3.9.24

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.
Files changed (47) hide show
  1. package/package.json +2 -2
  2. package/src/controllers/internalTrax.controller.js +84 -44
  3. package/src/controllers/mobileTrax.controller.js +43 -2
  4. package/src/controllers/teaxFlag.controller.js +4 -3
  5. package/src/controllers/trax.controller.js +5 -2
  6. package/src/controllers/traxDashboard.controllers.js +63 -54
  7. package/src/hbs/login-otp.hbs +943 -943
  8. package/src/logging/activityLogFlusher.js +59 -0
  9. package/src/logging/activityLogMiddleware.js +45 -0
  10. package/src/logging/activityLogStore.js +91 -0
  11. package/src/logging/compressBatches.js +83 -0
  12. package/src/logging/config.js +24 -0
  13. package/src/logging/createLoggableService.js +46 -0
  14. package/src/logging/logExternalCall.js +37 -0
  15. package/src/services/app.service.js +20 -14
  16. package/src/services/approver.service.js +28 -20
  17. package/src/services/authentication.service.js +12 -6
  18. package/src/services/camera.service.js +24 -18
  19. package/src/services/checklist.service.js +36 -31
  20. package/src/services/checklistAssign.service.js +44 -39
  21. package/src/services/checklistQuestion.service.js +40 -35
  22. package/src/services/checklistlog.service.js +40 -35
  23. package/src/services/clientRequest.service.js +12 -5
  24. package/src/services/clients.services.js +23 -18
  25. package/src/services/cluster.service.js +36 -28
  26. package/src/services/domain.service.js +33 -28
  27. package/src/services/download.services.js +48 -38
  28. package/src/services/group.service.js +28 -22
  29. package/src/services/lenskartEmployeeMapping.service.js +20 -15
  30. package/src/services/locus.service.js +41 -34
  31. package/src/services/notification.service.js +40 -31
  32. package/src/services/otp.service.js +24 -17
  33. package/src/services/planogram.service.js +12 -5
  34. package/src/services/processedTaskConfig.service.js +40 -32
  35. package/src/services/processedTaskList.service.js +36 -30
  36. package/src/services/processedchecklist.services.js +56 -48
  37. package/src/services/processedchecklistconfig.services.js +40 -35
  38. package/src/services/recurringFlagTracker.service.js +40 -33
  39. package/src/services/runAIFeatures.services.js +36 -31
  40. package/src/services/runAIRequest.services.js +44 -39
  41. package/src/services/store.service.js +36 -31
  42. package/src/services/tagging.service.js +12 -5
  43. package/src/services/taskConfig.service.js +40 -32
  44. package/src/services/teams.service.js +41 -30
  45. package/src/services/ticket.service.js +20 -15
  46. package/src/services/user.service.js +32 -25
  47. package/src/services/userAssignedstores.service.js +17 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "3.9.22",
3
+ "version": "3.9.24",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "path": "^0.12.7",
31
31
  "puppeteer": "^24.39.1",
32
32
  "swagger-ui-express": "^5.0.1",
33
- "tango-api-schema": "^2.6.6",
33
+ "tango-api-schema": "^2.6.12",
34
34
  "tango-app-api-middleware": "^3.5.2",
35
35
  "url": "^0.11.4",
36
36
  "winston": "^3.13.1",
@@ -325,7 +325,7 @@ export async function PCLconfigCreation( req, res ) {
325
325
  },
326
326
  } );
327
327
  let getSections = await CLquestions.aggregate( sectionQuery );
328
- if ( getSections.length || [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection' ].includes( getCLconfig.checkListType ) ) {
328
+ if ( getSections.length || getCLconfig.checkListType != 'custom' ) {
329
329
  if ( getSections.length ) {
330
330
  for ( let element3 of getSections ) {
331
331
  let collectQuestions = {};
@@ -624,6 +624,7 @@ export async function PCLconfigCreation( req, res ) {
624
624
  element4.rawImageUpload = getCLconfig?.rawImageUpload || false;
625
625
  element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
626
626
  element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
627
+ element4.userVerification = getCLconfig?.userVerification;
627
628
  }
628
629
  if ( userIdList.length ) {
629
630
  allQuestion = allQuestion.filter( ( item ) => typeof item._id == 'undefined' );
@@ -651,11 +652,11 @@ export async function PCLconfigCreation( req, res ) {
651
652
  // }
652
653
  }
653
654
  } else {
654
- if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection' ].includes( getCLconfig.checkListType ) ) {
655
+ if ( getCLconfig.checkListType != 'custom' ) {
655
656
  let storeNameList = allQuestion.map( ( item ) => item.store_id );
656
- let storeDetails = await storeService.find( { clientId: getCLconfig.client_id, status: 'active', ...( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection' ].includes( getCLconfig.checkListType ) ) ? { storeId: { $in: storeNameList } } : {} }, { storeId: 1 } );
657
+ let storeDetails = await storeService.find( { clientId: getCLconfig.client_id, status: 'active', ...( getCLconfig.checkListType != 'custom' ) ? { storeId: { $in: storeNameList } } : {} }, { storeId: 1 } );
657
658
  let storeList = storeDetails.map( ( store ) => store.storeId );
658
- if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection' ].includes( getCLconfig.checkListType ) ) {
659
+ if ( getCLconfig.checkListType != 'custom' ) {
659
660
  allQuestion = allQuestion.filter( ( ele ) => storeList.includes( ele?.store_id ) );
660
661
  } else {
661
662
  allQuestion = storeDetails.map( ( item ) => {
@@ -689,7 +690,7 @@ export async function PCLconfigCreation( req, res ) {
689
690
  client_id: getCLconfig.client_id,
690
691
  aiStoreList: allQuestion.length ? allQuestion.map( ( store ) => store.store_id ) : [],
691
692
  };
692
- if ( [ 'storeopenandclose', 'mobileusagedetection', 'uniformdetection', 'customerunattended', 'staffleftinthemiddle', 'scrum', 'cleaning', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection' ].includes( getCLconfig.checkListType ) ) {
693
+ if ( getCLconfig.checkListType != 'custom' ) {
693
694
  let processData = {
694
695
  aiStoreList: allQuestion.length ? allQuestion.map( ( store ) => {
695
696
  return { storeName: store.storeName, storeId: store.store_id, events: store.events };
@@ -2700,10 +2701,57 @@ export async function countUpdateRunAI( req, res ) {
2700
2701
  runAIFlag: req.body.runAICount,
2701
2702
  };
2702
2703
 
2704
+ // complianceCount is already populated for the other answer types; add the image/video scores on top.
2705
+ let complianceCount = getDetails.userComplianceCount || 0;
2706
+
2707
+ // Pull every "Matched/Not Matched" verdict out of a runAIData array, tolerating both shapes we store:
2708
+ // 1) [ { answerImage, results: [ { featureName, value } ] } ] (verdict nested under results)
2709
+ // 2) [ { featureName, value } ] (flat verdict)
2710
+ const extractMatchValues = ( runAIData ) => {
2711
+ if ( !Array.isArray( runAIData ) ) return [];
2712
+ const values = [];
2713
+ runAIData.forEach( ( entry ) => {
2714
+ const results = Array.isArray( entry?.results ) ? entry.results : [ entry ];
2715
+ results.forEach( ( r ) => {
2716
+ if ( r?.featureName === 'Matched/Not Matched' ) values.push( r?.value );
2717
+ } );
2718
+ } );
2719
+ return values;
2720
+ };
2721
+
2722
+ getDetails.questionAnswers.forEach( ( section ) => {
2723
+ section.questions.forEach( ( question ) => {
2724
+ if ( question.compliance && [ 'image', 'image/video' ].includes( question.answerType ) ) {
2725
+ // For 'image/video' each userAnswer carries its own answerType and runAIData only lives on
2726
+ // the image entries; a pure 'image' question keeps runAIData directly on every userAnswer.
2727
+ const aiAnswers = question.answerType === 'image/video' ?
2728
+ ( question.userAnswer || [] ).filter( ( ua ) => ua?.answerType === 'image' ) :
2729
+ ( question.userAnswer || [] );
2730
+
2731
+ // Compliance comes from the first configured answer: every "Matched/Not Matched" verdict
2732
+ // across the image answers must be True to count as matched, otherwise it's not matched.
2733
+ const matchValues = aiAnswers.flatMap( ( ua ) => extractMatchValues( ua?.runAIData ) );
2734
+ const allMatched = matchValues.length > 0 && matchValues.every( ( v ) => v === 'True' || v === true );
2735
+ const score = allMatched ?
2736
+ ( question.answers?.[0]?.matchedCount ?? 0 ) :
2737
+ ( question.answers?.[0]?.notMatchedCount ?? 0 );
2738
+
2739
+ // Persist the derived score back onto each userAnswer so PDF/dashboard reads stay consistent.
2740
+ ( question.userAnswer || [] ).forEach( ( ua ) => {
2741
+ ua.complianceScore = score;
2742
+ } );
2743
+
2744
+ complianceCount += score;
2745
+ }
2746
+ } );
2747
+ } );
2748
+
2749
+ updateData.complianceCount = complianceCount;
2750
+
2703
2751
  await processedchecklist.updateOne( { _id: req.body.id }, updateData );
2704
2752
  return res.sendSuccess( 'RunAI Count updated successfully' );
2705
2753
  } catch ( e ) {
2706
- logger.error( { functionName: 'updateRunAI', error: e } );
2754
+ logger.error( { functionName: 'countUpdateRunAI', error: e } );
2707
2755
  return res.sendError( e, 500 );
2708
2756
  }
2709
2757
  }
@@ -3532,6 +3580,7 @@ export async function runAIFlag( req, res ) {
3532
3580
  try {
3533
3581
  let checklistDetails = await CLconfig.find( {
3534
3582
  publish: true,
3583
+ client_id: '467',
3535
3584
  runAIQuestionCount: { $ne: 0 },
3536
3585
  $expr: {
3537
3586
  $gt: [
@@ -3540,47 +3589,38 @@ export async function runAIFlag( req, res ) {
3540
3589
  ],
3541
3590
  },
3542
3591
  }, { _id: 1, notifyFlags: 1, approver: 1 } );
3543
- let date = dayjs().subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
3592
+ let date = dayjs().format( 'YYYY-MM-DD' );
3544
3593
  if ( checklistDetails.length ) {
3545
3594
  await Promise.all( checklistDetails.map( async ( ele ) => {
3546
- let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, checklistStatus: 'submit', date_string: date }, { storeName: 1, checkListName: 1, questionAnswers: 1 } );
3595
+ let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, date_string: date }, { storeName: 1, checkListName: 1, userName: 1, submitTime_string: 1, runAIFlag: 1, questionFlag: 1, checklistStatus: 1 } );
3596
+ let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
3597
+ emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
3547
3598
  await Promise.all( submitDetails.map( ( store ) => {
3548
- if ( store.questionAnswers.length ) {
3549
- let runAIFlag = 0;
3550
- store.questionAnswers.forEach( ( section ) => {
3551
- section.questions.forEach( ( question ) => {
3552
- if ( question.answerType == 'image' && ( question?.userAnswer?.[0]?.runAIData?.value == 'False' || !question?.userAnswer?.[0]?.runAIData?.value ) ) {
3553
- runAIFlag++;
3554
- }
3555
- } );
3556
- } );
3557
- if ( runAIFlag ) {
3558
- let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
3559
- emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
3560
- let data = {
3561
- storeName: store.storeName,
3562
- flagCount: 1,
3563
- checklistName: store.checkListName?.trim(),
3564
- submittedBy: '--',
3565
- time: '--',
3566
- domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
3567
- type: 'delay',
3568
- };
3569
- const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
3570
- const htmlContent = handlebars.compile( fileContent );
3571
- const html = htmlContent( { data: data } );
3572
- emailList.forEach( ( email ) => {
3573
- let params = {
3574
- toEmail: email,
3575
- mailSubject: 'TangoEye | Checklist Flag',
3576
- htmlBody: html,
3577
- attachment: '',
3578
- sourceEmail: JSON.parse( process.env.SES ).adminEmail,
3579
- };
3580
- sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
3581
- } );
3582
- }
3583
- }
3599
+ let data = {
3600
+ storeName: store.storeName,
3601
+ flagCount: store?.runAIFlag + store?.questionFlag,
3602
+ runAIFlag: store?.runAIFlag,
3603
+ questionFlag: store?.questionFlag,
3604
+ checklistName: store.checkListName?.trim(),
3605
+ submittedBy: store?.userName,
3606
+ time: store?.submitTime_string,
3607
+ domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
3608
+ status: store.checklistStatus,
3609
+ };
3610
+ const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
3611
+ const htmlContent = handlebars.compile( fileContent );
3612
+ const html = htmlContent( { data: data } );
3613
+ emailList.forEach( ( email ) => {
3614
+ console.log( email );
3615
+ let params = {
3616
+ toEmail: email,
3617
+ mailSubject: 'TangoEye | Checklist Flag',
3618
+ htmlBody: html,
3619
+ attachment: '',
3620
+ sourceEmail: JSON.parse( process.env.SES ).adminEmail,
3621
+ };
3622
+ sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
3623
+ } );
3584
3624
  } ) );
3585
3625
  } ) );
3586
3626
  }
@@ -1586,7 +1586,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1586
1586
  let CLQAnswers = getChecklistQA.questionAnswers;
1587
1587
 
1588
1588
  if ( requestData.submittype == 'submit' ) {
1589
- if ( getChecklistQA?.redoEdit ) {
1589
+ if ( getChecklistQA?.redoEdit?.toString() != requestData?.redoEdit ) {
1590
1590
  requestData.submittype = 'draft';
1591
1591
  }
1592
1592
  reqAnswers.forEach( ( reqA ) => {
@@ -1677,6 +1677,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1677
1677
  let yn = [];
1678
1678
  if ( ( getChecklistQA?.redoEdit && qaAnswers[j].redo == requestSection[i].redo ) || !getChecklistQA?.redoEdit ) {
1679
1679
  for ( let k = 0; k < qaans.length; k++ ) {
1680
+ qaans[k].validationAnswer = '';
1680
1681
  if ( requestSection[i].answer == qaans[k].answer ) {
1681
1682
  if ( qaans[k].validationType == 'Capture Image' || qaans[k].validationType == 'Capture Video' ) {
1682
1683
  if ( requestSection[i].validationAnswer ) {
@@ -1696,6 +1697,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1696
1697
  if ( requestSection[i]?.runAIData && requestSection[i]?.runAIData?.length ) {
1697
1698
  qaans[k].runAIData = requestSection[i].runAIData;
1698
1699
  }
1700
+ if ( requestSection[i]?.runAIAnswer ) {
1701
+ qaans[k].runAIAnswer = requestSection[i].runAIAnswer;
1702
+ }
1699
1703
  yn.push( qaans[k] );
1700
1704
  }
1701
1705
  }
@@ -1737,6 +1741,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1737
1741
  };
1738
1742
  structure.answers.forEach( ( ans ) => {
1739
1743
  delete ans?.runAIData;
1744
+ delete ans?.runAIAnswer;
1740
1745
  } );
1741
1746
  newArray.push( structure );
1742
1747
  } else if ( qaAnswers[j].answerType == 'multiplechoicesingle' || ( qaAnswers[j].answerType == 'dropdown' && !qaAnswers[j].allowMultiple ) ) {
@@ -1744,6 +1749,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1744
1749
  let ms = [];
1745
1750
  if ( ( getChecklistQA?.redoEdit && qaAnswers[j].redo == requestSection[i].redo ) || !getChecklistQA?.redoEdit ) {
1746
1751
  for ( let k = 0; k < qaans.length; k++ ) {
1752
+ qaans[k].validationAnswer = '';
1747
1753
  if ( requestSection[i].answer == qaans[k].answer ) {
1748
1754
  if ( qaans[k].validationType == 'Capture Image' || qaans[k].validationType == 'Capture Video' ) {
1749
1755
  if ( requestSection[i].validationAnswer ) {
@@ -1771,6 +1777,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1771
1777
  };
1772
1778
  }
1773
1779
  }
1780
+ if ( requestSection[i]?.validationVideo?.length ) {
1781
+ for ( let video of requestSection[i].validationVideo ) {
1782
+ let validationAnswer = decodeURIComponent( video.split( '?' )[0] );
1783
+ if ( validationAnswer.length ) {
1784
+ let splitImgUrl = validationAnswer.split( '/' );
1785
+ if ( splitImgUrl.length > 1 ) {
1786
+ splitImgUrl.splice( 0, 3 );
1787
+ qaans[k].validationVideo.push( splitImgUrl.join( '/' ) || '' );
1788
+ }
1789
+ };
1790
+ }
1791
+ }
1774
1792
  }
1775
1793
  // qaans[k].descriptivetype = qaAnswers[j].descriptivetype || '';
1776
1794
  qaans[k].validationAnswer = requestSection[i].validationAnswer || '';
@@ -1778,6 +1796,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1778
1796
  if ( requestSection[i]?.runAIData && requestSection[i]?.runAIData?.length ) {
1779
1797
  qaans[k].runAIData = requestSection[i].runAIData;
1780
1798
  }
1799
+ if ( requestSection[i]?.runAIAnswer ) {
1800
+ qaans[k].runAIAnswer = requestSection[i].runAIAnswer;
1801
+ }
1781
1802
  ms.push( qaans[k] );
1782
1803
  }
1783
1804
  }
@@ -1820,6 +1841,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1820
1841
  };
1821
1842
  structure.answers.forEach( ( ans ) => {
1822
1843
  delete ans?.runAIData;
1844
+ delete ans?.runAIAnswer;
1823
1845
  } );
1824
1846
  newArray.push( structure );
1825
1847
  } else if ( qaAnswers[j].answerType == 'multiplechoicemultiple' || ( qaAnswers[j].answerType == 'dropdown' && qaAnswers[j].allowMultiple ) ) {
@@ -1827,6 +1849,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1827
1849
  let mcmo = [];
1828
1850
  if ( ( getChecklistQA?.redoEdit && qaAnswers[j].redo == requestSection[i].redo ) || !getChecklistQA?.redoEdit || ( getChecklistQA?.redoEdit && qaAnswers[j].answers.some( ( ans ) => ans.redo ) ) ) {
1829
1851
  for ( let k = 0; k < qaans.length; k++ ) {
1852
+ qaans[k].validationAnswer = '';
1830
1853
  let separatedArray = typeof requestSection[i].Multianswer == 'string' ? JSON.parse( requestSection[i].Multianswer ) : requestSection[i].Multianswer;
1831
1854
  for ( let s = 0; s < separatedArray.length; s++ ) {
1832
1855
  if ( ( separatedArray[s].answer == qaans[k].answer ) ) {
@@ -1849,6 +1872,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1849
1872
  if ( separatedArray[s]?.runAIData && separatedArray[s]?.runAIData?.length ) {
1850
1873
  qaans[k].runAIData = separatedArray[s].runAIData;
1851
1874
  }
1875
+ if ( separatedArray[s]?.runAIAnswer ) {
1876
+ qaans[k].runAIAnswer = separatedArray[s].runAIAnswer;
1877
+ }
1852
1878
  mcmo.push( qaans[k] );
1853
1879
  }
1854
1880
  }
@@ -1899,6 +1925,7 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1899
1925
  };
1900
1926
  structure.answers.forEach( ( ans ) => {
1901
1927
  delete ans?.runAIData;
1928
+ delete ans?.runAIAnswer;
1902
1929
  } );
1903
1930
  newArray.push( structure );
1904
1931
  } else if ( [ 'image/video', 'multipleImage' ].includes( qaAnswers[j].answerType ) ) {
@@ -1938,6 +1965,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
1938
1965
  if ( separatedArray[s]?.runAIData && separatedArray[s]?.runAIData?.length ) {
1939
1966
  newAnswer.runAIData = separatedArray[s].runAIData;
1940
1967
  }
1968
+ if ( separatedArray[s]?.runAIAnswer ) {
1969
+ newAnswer.runAIAnswer = separatedArray[s].runAIAnswer;
1970
+ }
1941
1971
  mcmi.push( newAnswer );
1942
1972
  }
1943
1973
  }
@@ -2049,6 +2079,9 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2049
2079
  if ( requestSection[i]?.runAIData && requestSection[i]?.runAIData?.length ) {
2050
2080
  ansstructure.runAIData = requestSection[i].runAIData;
2051
2081
  }
2082
+ if ( requestSection[i]?.runAIAnswer ) {
2083
+ ansstructure.runAIAnswer = requestSection[i].runAIAnswer;
2084
+ }
2052
2085
  des.push( ansstructure );
2053
2086
  }
2054
2087
  }
@@ -2106,12 +2139,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2106
2139
  logger.error( { message: requestData.questionAnswers, error: 'QuestionanswersPayload' } );
2107
2140
 
2108
2141
  if ( requestData.submittype == 'submit' ) {
2142
+ requestData.userComplianceCount = 0;
2109
2143
  for ( let section of sectionFormat ) {
2110
2144
  for ( let question of section.questions ) {
2111
2145
  let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
2112
2146
  if ( mustValidate && ( !question.userAnswer || !question.userAnswer.length ) ) {
2113
2147
  return res.sendError( 'Please Fill All Fields', 400 );
2114
2148
  }
2149
+ if ( question.compliance ) {
2150
+ const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
2151
+ let score = scores.length ? Math.max( ...scores ) : 0;
2152
+ requestData.userComplianceCount += score;
2153
+ }
2115
2154
  }
2116
2155
  }
2117
2156
  }
@@ -2755,6 +2794,7 @@ export async function submitChecklist( req, res ) {
2755
2794
  updateData.checklistStatus = 'submit';
2756
2795
  updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
2757
2796
  updateData.submitMobileTime = requestData?.currentTime;
2797
+ updateData.userComplianceCount = requestData?.userComplianceCount;
2758
2798
  }
2759
2799
  // updateData.questionAnswers.forEach( ( section ) => {
2760
2800
  // section.questions.forEach( ( question ) => {
@@ -2884,7 +2924,7 @@ export async function submitChecklist( req, res ) {
2884
2924
  }
2885
2925
  }
2886
2926
  }
2887
- if ( getchecklist?.[0]?.redoEdit ) {
2927
+ if ( getchecklist?.[0]?.redoEdit?.toString() != requestData?.redoEdit ) {
2888
2928
  return res.sendSuccess( 'New Questions Added So,Checklist moved back to In Progress. Please complete the updated checklist.' );
2889
2929
  } else {
2890
2930
  return res.sendSuccess( 'Checklist Updated Successfully' );
@@ -4155,6 +4195,7 @@ export async function questionList( req, res ) {
4155
4195
  taskType: { $ifNull: [ '$planoType', '' ] },
4156
4196
  streamId: { $ifNull: [ '$streamId', '' ] },
4157
4197
  export: { $ifNull: [ '$export', false ] },
4198
+ redoEdit: 1,
4158
4199
  },
4159
4200
  } );
4160
4201
 
@@ -2747,7 +2747,7 @@ export const checklistDropdownV1 = async ( req, res ) => {
2747
2747
  $or: [
2748
2748
  { questionFlag: { $gte: 1 } },
2749
2749
  { timeFlag: { $gte: 1 } },
2750
- { checkListType: { $in: [ 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection' ] } },
2750
+ { checkListType: { $in: [ 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection', 'vehicle_check_in' ] } },
2751
2751
  {
2752
2752
  runAIQuestionCount: { $gte: 1 },
2753
2753
  },
@@ -3051,7 +3051,7 @@ export const flagTablesV2 = async ( req, res ) => {
3051
3051
  }
3052
3052
 
3053
3053
  if ( requestData?.filter === 'all' ) {
3054
- findAndQuery.push( { $or: [ { checkListType: { $in: [ 'custom', 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection' ] } } ] } );
3054
+ 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' ] } } ] } );
3055
3055
  } else if ( requestData?.filter === 'question' ) {
3056
3056
  findAndQuery.push( { checkListType: 'custom' } );
3057
3057
  findAndQuery.push( { questionFlag: { $gte: 1 } } );
@@ -3059,7 +3059,7 @@ export const flagTablesV2 = async ( req, res ) => {
3059
3059
  findAndQuery.push( { checkListType: 'custom' } );
3060
3060
  findAndQuery.push( { timeFlag: { $gte: 1 } } );
3061
3061
  } else if ( requestData?.filter === 'detection' ) {
3062
- findAndQuery.push( { checkListType: { $in: [ 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection' ] } } );
3062
+ findAndQuery.push( { checkListType: { $in: [ 'customerunattended', 'mobileusagedetection', 'staffleftinthemiddle', 'storeopenandclose', 'uniformdetection', 'cleaning', 'scrum', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'storehygienemonitoring', 'unattendeddetection', 'vehicle_check_in' ] } } );
3063
3063
  } else if ( requestData?.filter === 'runAI' ) {
3064
3064
  if ( req.body.runAIChecklistName ) {
3065
3065
  findAndQuery.push( { checkListName: { $in: req.body.runAIChecklistName } } );
@@ -3200,6 +3200,7 @@ export const flagTablesV2 = async ( req, res ) => {
3200
3200
  } );
3201
3201
 
3202
3202
  let getTotalCount = await processedchecklistService.aggregate( findQuery );
3203
+ console.log( getTotalCount );
3203
3204
  if ( !getTotalCount.length ) {
3204
3205
  return res.sendError( { error: 'No Data Found' }, 204 );
3205
3206
  }
@@ -614,10 +614,13 @@ export const zoneList = async ( req, res ) => {
614
614
  'Employee Count Detection',
615
615
  'Staff Grouping Compliance',
616
616
  'Retail Shelf Monitoring',
617
+ 'Store Mopping Compliance Checklist',
618
+ 'Occupancy Detection',
617
619
  ];
618
620
  const allowedChecklistsStreams = [
619
621
  'Camera Angle Change Compliance',
620
622
  ];
623
+ console.log( allowedChecklists.includes( inputBody.checkListName ) );
621
624
 
622
625
  if ( inputBody.checkListName && allowedChecklists.includes( inputBody.checkListName ) ) {
623
626
  console.log( '==================' );
@@ -1912,7 +1915,6 @@ export const updateConfigure = async ( req, res ) => {
1912
1915
 
1913
1916
  export const updateConfigurev1 = async ( req, res ) => {
1914
1917
  try {
1915
- console.log( '123' );
1916
1918
  let inputBody = req.body;
1917
1919
  let id;
1918
1920
  let checklistDetails;
@@ -3970,6 +3972,7 @@ async function insertPCBulkV4( getCLconfig, checklistId, currentdate, updatedche
3970
3972
  element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
3971
3973
  element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
3972
3974
  element4.complianceCount = getCLconfig?.complianceCount || 0;
3975
+ element4.userVerification = getCLconfig?.userVerification;
3973
3976
  assignUserList.push( { ...element4 } );
3974
3977
  }
3975
3978
  } ) );
@@ -4134,7 +4137,7 @@ async function updateOpenSearch( user, data ) {
4134
4137
  export const aiChecklist = async ( req, res ) => {
4135
4138
  try {
4136
4139
  let storeDetails = await storeService.count( { clientId: req.query.clientId, status: 'active' } );
4137
- let aiList = [ 'mobileusagedetection', 'storeopenandclose', 'uniformdetection', 'staffleftinthemiddle', 'customerunattended', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection' ];
4140
+ let aiList = [ 'mobileusagedetection', 'storeopenandclose', 'uniformdetection', 'staffleftinthemiddle', 'customerunattended', 'eyetest', 'remoteoptometrist', 'storehygienemonitoring', 'cleaning', 'scrum', 'suspiciousactivity', 'suspiciousfootfall', 'drinking', 'bagdetection', 'inventorycount', 'carsattended', 'numberplateinfo', 'vehicle_check_in', 'outsidebusinesshoursqueuetracking', 'halfshutter', 'tvcompliance', 'cameratampering', 'queuealert', 'staffgrouping', 'boxalert', 'employeeCount', 'unattendeddetection', 'occupancydetection' ];
4138
4141
  let checklistDetails = [];
4139
4142
  let publishList = [];
4140
4143
  let unpublishList = [];
@@ -276,29 +276,10 @@ export const checklistPerformance = async ( req, res ) => {
276
276
  toDate = new Date( toDate.getTime() - userTimezoneOffset );
277
277
  toDate.setUTCHours( 23, 59, 59, 59 );
278
278
  let result = {};
279
- let checklistIdList = [];
280
279
 
281
280
  let limit = parseInt( requestData?.limit ) || 10;
282
281
  let skip = limit * ( requestData?.offset ) || 0;
283
282
 
284
- const detectionPayload = {
285
- 'fromDate': requestData.fromDate,
286
- 'toDate': requestData.toDate,
287
- 'clientId': requestData.clientId,
288
- 'sortColumnName': requestData.sortColumnName,
289
- 'sortBy': requestData.sortBy,
290
- 'storeId': requestData.storeId,
291
- };
292
-
293
-
294
- let complianceURL = JSON.parse( process.env.LAMBDAURL ).complianceURL;
295
- const complianceData = await LamdaServiceCall( complianceURL, detectionPayload );
296
- if ( complianceData?.data?.length && requestData?.sortColumnName == 'questionCompliance' ) {
297
- const end = skip + requestData?.limit;
298
- checklistIdList = complianceData.data.slice( skip, end )?.map( ( ele ) => ele?.sourceCheckList_id );
299
- }
300
-
301
-
302
283
  // Get User Based Checklist //
303
284
  // let loginUser = { clientId: requestData.clientId, role: req.user.role, userType: req.user.userType, userEmail: req.user.email };
304
285
  // let getUserEmails = await getChecklistUsers( loginUser );
@@ -314,10 +295,6 @@ export const checklistPerformance = async ( req, res ) => {
314
295
  { $or: [ { store_id: { $in: requestData.storeId } }, { store_id: { $eq: '' }, userEmail: { $in: requestData.userEmailes } } ] },
315
296
  );
316
297
 
317
- if ( requestData?.sortColumnName == 'questionCompliance' ) {
318
- findAndQuery.push( { sourceCheckList_id: { $in: checklistIdList } } );
319
- }
320
-
321
298
  findQuery.push( { $match: { $and: findAndQuery } } );
322
299
 
323
300
  if ( requestData.searchValue && requestData.searchValue != '' ) {
@@ -367,6 +344,16 @@ export const checklistPerformance = async ( req, res ) => {
367
344
  timeFlag: { $sum: '$timeFlag' },
368
345
  questionFlagCount: { $sum: '$questionFlag' },
369
346
  runAIFlagCount: { $sum: '$runAIFlag' },
347
+ userComplianceCountTotal: {
348
+ $sum: {
349
+ $cond: [
350
+ { $eq: [ '$checklistStatus', 'submit' ] },
351
+ { $ifNull: [ '$userComplianceCount', 0 ] },
352
+ 0,
353
+ ],
354
+ },
355
+ },
356
+ complianceCount: { $first: '$complianceCount' },
370
357
  checkListType: { $last: '$checkListType' },
371
358
  redo: { $sum: { $cond: [ { $eq: [ '$redoStatus', true ] }, 1, 0 ] } },
372
359
  task: {
@@ -426,6 +413,36 @@ export const checklistPerformance = async ( req, res ) => {
426
413
  checkListType: 1,
427
414
  redo: 1,
428
415
  task: 1,
416
+ questionCompliance: {
417
+ $let: {
418
+ vars: {
419
+ divisor: {
420
+ $cond: [
421
+ { $gt: [ '$submittedChecklist', 1 ] },
422
+ { $multiply: [ { $ifNull: [ '$complianceCount', 0 ] }, '$submittedChecklist' ] },
423
+ { $ifNull: [ '$complianceCount', 0 ] },
424
+ ],
425
+ },
426
+ },
427
+ in: {
428
+ $cond: [
429
+ { $gt: [ '$$divisor', 0 ] },
430
+ {
431
+ $round: [
432
+ {
433
+ $multiply: [
434
+ { $divide: [ '$userComplianceCountTotal', '$$divisor' ] },
435
+ 100,
436
+ ],
437
+ },
438
+ 0,
439
+ ],
440
+ },
441
+ 0,
442
+ ],
443
+ },
444
+ },
445
+ },
429
446
  },
430
447
  } );
431
448
 
@@ -450,14 +467,6 @@ export const checklistPerformance = async ( req, res ) => {
450
467
  return res.sendError( 'no data found', 204 );
451
468
  }
452
469
 
453
- getChecklistPerformanceData?.[0]?.data.forEach( ( ele ) => {
454
- let findCompliance;
455
- if ( complianceData?.data?.length ) {
456
- findCompliance = complianceData?.data?.find( ( data ) => data.sourceCheckList_id == ele?.sourceCheckList_id );
457
- }
458
- ele['questionComplianceRate'] = findCompliance?.compliancePercentage ?? 0;
459
- } );
460
-
461
470
  if ( requestData.export ) {
462
471
  const exportdata = [];
463
472
  getChecklistPerformanceData[0].data.forEach( ( element ) => {
@@ -4335,26 +4344,26 @@ function escapeRegex( text ) {
4335
4344
  // }
4336
4345
  // }
4337
4346
 
4338
- async function LamdaServiceCall( url, data ) {
4339
- try {
4340
- const requestOptions = {
4341
- method: 'POST',
4342
- headers: {
4343
- 'Content-Type': 'application/json',
4344
- },
4345
- body: JSON.stringify( data ),
4346
- };
4347
- console.log( data );
4348
- const response = await fetch( url, requestOptions );
4349
- if ( !response.ok ) {
4350
- throw new Error( `Response status: ${response.status}` );
4351
- return false;
4352
- }
4353
- const json = await response.json();
4354
- return json;
4355
- } catch ( error ) {
4356
- console.log( error );
4357
- logger.error( { error: error, message: data, function: 'LamdaServiceCall' } );
4358
- return false;
4359
- }
4360
- }
4347
+ // async function LamdaServiceCall( url, data ) {
4348
+ // try {
4349
+ // const requestOptions = {
4350
+ // method: 'POST',
4351
+ // headers: {
4352
+ // 'Content-Type': 'application/json',
4353
+ // },
4354
+ // body: JSON.stringify( data ),
4355
+ // };
4356
+ // console.log( data );
4357
+ // const response = await fetch( url, requestOptions );
4358
+ // if ( !response.ok ) {
4359
+ // throw new Error( `Response status: ${response.status}` );
4360
+ // return false;
4361
+ // }
4362
+ // const json = await response.json();
4363
+ // return json;
4364
+ // } catch ( error ) {
4365
+ // console.log( error );
4366
+ // logger.error( { error: error, message: data, function: 'LamdaServiceCall' } );
4367
+ // return false;
4368
+ // }
4369
+ // }