tango-app-api-audit 3.4.0-alpha.1 → 3.4.0-alpha.3

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.
@@ -1,7 +1,15 @@
1
- import { checkFileExist, getDate, insertOpenSearchData, listFileByPath, logger, signedUrl, sqsReceive } from 'tango-app-api-middleware';
2
- import { aggregateBinaryAuditModel, createBinaryAudit } from '../service/binaryAudit.service.js';
1
+ import { checkFileExist, download, getDate, getDateWithCustomizeTime, getUTC, insertOpenSearchData, listFileByPath, logger, signedUrl, sqsReceive } from 'tango-app-api-middleware';
2
+ import { aggregateBinaryAudit, createBinaryAudit, updateOneBinaryAuditModel } from '../service/binaryAudit.service.js';
3
3
  import { findOneStore } from '../service/store.service.js';
4
4
  import { findOneUser } from '../service/user.service.js';
5
+ import { aggregateUserEmpDetection, createUserEmpDetection } from '../service/userEmpDetection.service.js';
6
+ import { aggregateTraxAuditData, findOneTraxAuditData } from '../service/traxAuditData.service.js';
7
+ import { aggregateClient } from '../service/client.service.js';
8
+ import dayjs from 'dayjs';
9
+ import { aggregateAssignAudit } from '../service/assignAudit.service.js';
10
+ import { aggregateStoreEmpDetection } from '../service/storeEmpDetection.service.js';
11
+ import { listQueue } from 'tango-app-api-middleware/src/utils/aws/sqs.js';
12
+ import _ from 'lodash';
5
13
 
6
14
 
7
15
  export async function getDetectionAuditFile( req, res ) {
@@ -37,7 +45,7 @@ export async function getDetectionAuditFile( req, res ) {
37
45
  },
38
46
  ];
39
47
 
40
- const userDetails = await aggregateUserAudit( userQuery );
48
+ const userDetails = await aggregateUserEmpDetection( userQuery );
41
49
  logger.info( { userDetails: userDetails, function: 'userDetails' } );
42
50
  const auditStatus =
43
51
  userDetails && userDetails.length > 0 ? userDetails[0].auditStatus : null;
@@ -380,7 +388,7 @@ export async function getDetectionAuditFile( req, res ) {
380
388
  moduleType: inputData.moduleType,
381
389
  };
382
390
 
383
- insertData = await createUserAudit( record );
391
+ insertData = await createUserEmpDetection( record );
384
392
  await updateManyStoreAudit( query, storeRecord );
385
393
  } else {
386
394
  insertData = userDetails[0];
@@ -451,6 +459,403 @@ export async function getDetectionAuditFile( req, res ) {
451
459
  }
452
460
  }
453
461
 
462
+ export async function workSpace( req, res ) {
463
+ try {
464
+ const inputData = req.body;
465
+ const limit = inputData.limit || 10;
466
+ const offset = inputData.offset ?
467
+ ( inputData.offset - 1 ) * limit :
468
+ 0;
469
+ const dateRange = await getDate( new Date(), new Date() );
470
+ const filter =[
471
+ { status: { $in: [ 'active', 'hold' ] } },
472
+ { 'auditConfigs.audit': { $eq: true } },
473
+ ];
474
+ if ( inputData?.clientId?.length > 0 ) {
475
+ filter.push( { clientId: { $in: inputData.clientId } } );
476
+ }
477
+ const temp = [];
478
+ const clientQuery = [
479
+ {
480
+ $match: {
481
+ $and: filter,
482
+ },
483
+ },
484
+ {
485
+ $project: {
486
+ clientName: 1,
487
+ clientId: 1,
488
+ queueName: {
489
+ $cond: [
490
+ { $eq: [ inputData.moduleType, 'unattended-customer' ] }, '$auditConfigs.traxQueueName.unattendedCustomer',
491
+ { $cond: [
492
+ { $eq: [ inputData.moduleType, 'left-in-middle' ] }, '$auditConfigs.traxQueueName.leftInMiddle',
493
+ {
494
+ $cond: [
495
+ { $eq: [ inputData.moduleType, 'uniform-detection' ] }, '$auditConfigs.traxQueueName.uniformDetection',
496
+ {
497
+ $cond: [
498
+ { $eq: [ inputData.moduleType, 'mobile-detection' ] }, '$auditConfigs.traxQueueName.mobileDetection', '$auditConfigs.traxQueueName.cameraAngleChange',
499
+ ],
500
+ },
501
+ ],
502
+ },
503
+ ],
504
+ },
505
+ ],
506
+ },
507
+ },
508
+ },
509
+ ];
510
+
511
+
512
+ if ( inputData.searchValue && inputData.searchValue !== '' ) {
513
+ clientQuery.push( {
514
+ $match: {
515
+ $or: [
516
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
517
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
518
+ ],
519
+ },
520
+ } );
521
+ }
522
+ if ( inputData.sortColumnName ) {
523
+ const sortBy = inputData.sortBy || -1;
524
+ const sortColumn = inputData.sortColumnName;
525
+ clientQuery.push( {
526
+ $sort: { [sortColumn]: sortBy },
527
+ },
528
+ );
529
+ }
530
+
531
+ const count = await aggregateClient( clientQuery );
532
+ if ( count?.length == 0 ) {
533
+ return res.sendError( 'No Data Found', 204 );
534
+ }
535
+
536
+ if ( !inputData.isExport ) {
537
+ clientQuery.push( { $skip: offset }, { $limit: limit } );
538
+ }
539
+
540
+ const clientDetails = await aggregateClient( clientQuery );
541
+ const clientList = clientDetails.map( ( i ) => i.clientId );
542
+ const fileDate = dayjs( dateRange.start ).subtract( 1, 'days' );
543
+ const getStoreData = await findOneTraxAuditData(
544
+ {},
545
+ { fileDate: 1 },
546
+ { createdAt: -1 },
547
+ );
548
+ const latestDate = getStoreData?.fileDate || dayjs( fileDate ).format( 'DD-MM-YYYY' );
549
+ const auditStoreDataQuery = [
550
+ {
551
+ $match: {
552
+ clientId: { $in: clientList },
553
+ moduleType: { $eq: inputData.moduleType },
554
+ fileDate: { $eq: latestDate },
555
+ },
556
+ },
557
+ {
558
+ $group: {
559
+ _id: '$clientId',
560
+ clientId: { $last: '$clientId' },
561
+ clientName: { $last: '$clientName' },
562
+ installedStore: { $last: '$installedStore' },
563
+ // queueName: { $last: '$queueName' },
564
+ totalCount: { $sum: 1 },
565
+ },
566
+ },
567
+ {
568
+ $project: {
569
+ _id: 0,
570
+ clientId: 1,
571
+ clientName: 1,
572
+ installedStore: 1,
573
+ queueName: 1,
574
+ totalCount: 1,
575
+ },
576
+ },
577
+ ];
578
+
579
+ const completedStores = [
580
+ {
581
+ $match: {
582
+ $and: [
583
+ { clientId: { $in: clientList } },
584
+ { fileDate: { $eq: latestDate } },
585
+ { moduleType: { $eq: inputData.moduleType } },
586
+ ],
587
+ },
588
+ },
589
+ {
590
+ $project: {
591
+ clientId: 1,
592
+ completed: {
593
+ $cond: [ { $eq: [ '$status', 'completed' ] }, 1, 0 ],
594
+ },
595
+ },
596
+ },
597
+ // {
598
+ // $group: {
599
+ // _id: { clientId: '$clientId', storeId: '$storeId' },
600
+ // clientId: { $first: '$clientId' },
601
+ // completedStoresCount: { $sum: '$completed' },
602
+ // },
603
+ // },
604
+ {
605
+ $group: {
606
+ _id: '$clientId',
607
+ clientId: { $first: '$clientId' },
608
+ // completedStoresCount: { $first: '$completedStoresCount' },
609
+ completedStoresCount: { $sum: '$completed' },
610
+ },
611
+ },
612
+ {
613
+ $project: {
614
+ _id: 0,
615
+ clientId: 1,
616
+ completedStoresCount: 1,
617
+ },
618
+ },
619
+ ];
620
+
621
+ const auditFiles = [
622
+ {
623
+ $match: {
624
+ $and: [
625
+ { clientId: { $in: clientList } },
626
+ { createdAt: { $gte: dateRange.start } },
627
+ { createdAt: { $lte: dateRange.end } },
628
+ { moduleType: { $eq: inputData.moduleType } },
629
+ ],
630
+ },
631
+ },
632
+ {
633
+ $project: {
634
+ clientId: 1,
635
+ pending: {
636
+ $cond: [
637
+ { $or: [ { $in: [ '$auditStatus', [ 'inprogress', 'drafted' ] ] } ] },
638
+ 1,
639
+ 0,
640
+ ],
641
+ },
642
+ },
643
+ },
644
+ {
645
+ $group: {
646
+ _id: { clientId: '$clientId', storeId: '$storeId' },
647
+ clientId: { $first: '$clientId' },
648
+ pendingCount: { $sum: '$pending' },
649
+ totalFilesCount: { $sum: 1 },
650
+ },
651
+ },
652
+ {
653
+ $group: {
654
+ _id: '$clientId',
655
+ clientId: { $first: '$clientId' },
656
+ pendingCount: { $first: { $ifNull: [ '$pendingCount', 0 ] } },
657
+ totalFilesCount: { $first: '$totalFilesCount' },
658
+ },
659
+ },
660
+ ];
661
+ const userIncompleteFiles = [
662
+ {
663
+ $match: {
664
+ $and: [
665
+ { clientId: { $in: clientList } },
666
+ { userId: { $eq: req.user._id } },
667
+ { moduleType: { $eq: inputData.moduleType } },
668
+ { createdAt: { $gte: dateRange.start } },
669
+ { createdAt: { $lte: dateRange.end } },
670
+ ],
671
+ },
672
+ },
673
+ {
674
+ $sort: {
675
+ updatedAt: -1,
676
+ },
677
+ },
678
+ {
679
+ $project: {
680
+ clientId: 1,
681
+ isDraft: 1,
682
+ draft: {
683
+ $cond: [ { $eq: [ '$auditStatus', 'drafted' ] }, 1, 0 ],
684
+ },
685
+ inprogress: {
686
+ $cond: [ { $eq: [ '$auditStatus', 'inprogress' ] }, 1, 0 ],
687
+ },
688
+ },
689
+ },
690
+ {
691
+ $group: {
692
+ _id: '$clientId',
693
+ clientId: { $first: '$clientId' },
694
+ isDrafted: { $first: '$isDraft' },
695
+ userDraftCount: { $sum: '$draft' },
696
+ userInprogressCount: { $sum: '$inprogress' },
697
+ },
698
+ },
699
+ ];
700
+
701
+ const userAsignAudit = [
702
+ {
703
+ $match: {
704
+ $and: [
705
+ { userId: { $eq: req.user._id } },
706
+ { isCompleted: false },
707
+ { clientId: { $in: clientList } },
708
+ { moduleType: { $eq: inputData.moduleType } },
709
+ ],
710
+ },
711
+ },
712
+ {
713
+ $group: {
714
+ _id: '$clientId',
715
+ clientId: { $first: '$clientId' },
716
+ userAsignedCount: { $sum: 1 },
717
+ },
718
+ },
719
+ {
720
+ $project: {
721
+ _id: 0,
722
+ clientId: 1,
723
+ userAsignedCount: 1,
724
+ },
725
+ },
726
+ ];
727
+
728
+ const clientAssign = [
729
+ {
730
+ $match: {
731
+ $and: [ { isCompleted: false },
732
+ { clientId: { $in: clientList } },
733
+ { moduleType: { $eq: inputData.moduleType } },
734
+ ],
735
+ },
736
+ },
737
+ {
738
+ $group: {
739
+ _id: '$clientId',
740
+ clientId: { $first: '$clientId' },
741
+ clientAsignedCount: { $sum: 1 },
742
+ },
743
+ },
744
+ {
745
+ $project: {
746
+ _id: 0,
747
+ clientId: 1,
748
+ clientAsignedCount: { $ifNull: [ '$clientAsignedCount', 0 ] },
749
+ },
750
+ },
751
+ ];
752
+
753
+ const auditUserCount = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateUserEmpDetection( auditFiles ) : await aggregateBinaryAudit( auditFiles );
754
+ const userIncompleteFilesCount = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateUserEmpDetection( userIncompleteFiles ) : await aggregateBinaryAudit( userIncompleteFiles );
755
+ const userAsignAuditCount = await aggregateAssignAudit( userAsignAudit );
756
+ const completedStoresCount = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )? await aggregateStoreEmpDetection( completedStores ) : await aggregateBinaryAudit( completedStores );
757
+ const clientAssignedCount = await aggregateAssignAudit( clientAssign );
758
+ const auditStoreData = await aggregateTraxAuditData(
759
+ auditStoreDataQuery,
760
+ );
761
+
762
+ const mergeAll = _.merge(
763
+ _.keyBy( clientDetails, 'clientId' ),
764
+ _.keyBy( auditUserCount, 'clientId' ),
765
+ _.keyBy( userIncompleteFilesCount, 'clientId' ),
766
+ _.keyBy( clientAssignedCount, 'clientId' ),
767
+ );
768
+ const finalResult = _.values( mergeAll );
769
+ const merge = _.values(
770
+ _.merge(
771
+ _.keyBy( finalResult, 'clientId' ),
772
+ _.keyBy( auditStoreData, 'clientId' ),
773
+ _.keyBy( userAsignAuditCount, 'clientId' ),
774
+ _.keyBy( completedStoresCount, 'clientId' ),
775
+ ),
776
+ );
777
+ let totalStores = 0;
778
+ for ( let i = 0; i < merge.length; i++ ) {
779
+ totalStores = merge[i]?.totalCount || 0;
780
+
781
+ const pending = await listQueue( merge[i].queueName );
782
+
783
+ if ( !pending.statusCode ) {
784
+ temp.push( {
785
+ clientId: merge[i].clientId,
786
+ clientName: merge[i].clientName,
787
+ completedStoresCount: merge[i].completedStoresCount ?
788
+ Number( merge[i].completedStoresCount ) :
789
+ 0,
790
+ pendingByQueue: Number( pending ),
791
+ pendingByUser:
792
+ merge[i].pendingCount >= 0 ?
793
+ Number( merge[i].pendingCount ) +
794
+ ( merge[i].clientAsignedCount ?
795
+ Number( merge[i].clientAsignedCount ) :
796
+ 0 ) :
797
+ 0,
798
+ totalCount: merge[i].totalCount,
799
+ queueName: merge[i].queueName,
800
+ isDraft: merge[i]?.isDrafted || false,
801
+ isEnable:
802
+ Number( pending ) > 0 ||
803
+ merge[i].isDrafted ||
804
+ ( merge[i].userAsignedCount && merge[i].userAsignedCount > 0 ) ||
805
+ ( merge[i].userInprogressCount && merge[i].userInprogressCount > 0 ) ?
806
+ 1 :
807
+ 0,
808
+ completedRatio: merge[i].totalCount ?
809
+ merge[i].completedStoresCount ?
810
+ Math.round(
811
+ ( Number( merge[i].completedStoresCount ) /
812
+ Number( merge[i].totalCount ) ) *
813
+ 100,
814
+ ) :
815
+ 0 :
816
+ 0,
817
+ isAssigned:
818
+ merge[i].userAsignedCount && merge[i].userAsignedCount > 0 ?
819
+ true :
820
+ false,
821
+ Assignedcount:
822
+ merge[i].userAsignedCount && merge[i].userAsignedCount > 0 ?
823
+ merge[i].userAsignedCount :
824
+ 0,
825
+ } );
826
+ }
827
+ }
828
+
829
+ if ( inputData.isExport ) {
830
+ const exportdata = [];
831
+ temp.forEach( ( element ) => {
832
+ exportdata.push( {
833
+ 'Client Name': element.clientName,
834
+ 'Client Id': element.clientId,
835
+ 'Completed stores Count': element.completedStoresCount,
836
+ 'Completed Percentage': element.completedRatio + ' %',
837
+ 'Pending Queue': element.pendingByQueue,
838
+ 'Pending User': element.pendingByUser,
839
+ 'Draft': element.isDraft ? 1 : 0,
840
+ 'Assigned': element.Assignedcount,
841
+ } );
842
+ } );
843
+ await download( exportdata, res );
844
+ return;
845
+ } else {
846
+ return res.sendSuccess( {
847
+ result: temp,
848
+ count: count.length,
849
+ totalStores: totalStores,
850
+ } );
851
+ }
852
+ } catch ( error ) {
853
+ const err = error.message || 'Internal Server Error';
854
+ logger.info( { error: error, message: req.query, function: 'workSpace' } );
855
+ return res.sendError( err, 500 );
856
+ }
857
+ }
858
+
454
859
  export async function getAuditFile( req, res ) {
455
860
  try {
456
861
  const inputData = req.query;
@@ -480,7 +885,7 @@ export async function getAuditFile( req, res ) {
480
885
  },
481
886
  ];
482
887
 
483
- const userDetails = await aggregateBinaryAuditModel( userQuery );
888
+ const userDetails = await aggregateBinaryAudit( userQuery );
484
889
  logger.info( { userDetails: userDetails, function: 'userDetails' } );
485
890
  const auditStatus = userDetails && userDetails.length > 0 ? userDetails[0].auditStatus : null;
486
891
 
@@ -728,6 +1133,7 @@ export async function getAuditFile( req, res ) {
728
1133
  question: msg.question,
729
1134
  questionType: msg.questionType,
730
1135
  streamName: msg.streamName,
1136
+ auditId: insertData._id,
731
1137
  } );
732
1138
  } catch ( error ) {
733
1139
  const err = error.message || 'Internal Server Error';
@@ -735,3 +1141,1155 @@ export async function getAuditFile( req, res ) {
735
1141
  return res.sendError( err, 500 );
736
1142
  }
737
1143
  }
1144
+
1145
+ export async function saveBinary( req, res ) {
1146
+ try {
1147
+ const inputData = req.body;
1148
+
1149
+ switch ( inputData.moduleType ) {
1150
+ case 'unattended-customer':
1151
+ let unattendedquery ={
1152
+ _id: inputData.auditId,
1153
+ storeId: inputData.storeId,
1154
+ fileDate: inputData.fileDate,
1155
+ tempId: inputData.tempId,
1156
+ moduleType: inputData.moduleType,
1157
+ };
1158
+
1159
+ let update={
1160
+ auditStatus: 'completed',
1161
+ answer: inputData.answer,
1162
+ userCommands: inputData.userComments || '',
1163
+ };
1164
+ let updatDataunattended= await updateOneBinaryAuditModel( unattendedquery, update );
1165
+ if ( updatDataunattended.modifiedCount==0 ) {
1166
+ return res.sendError( updatDataunattended, 204 );
1167
+ }
1168
+ logger.info( 'update in binary data', { query: unattendedquery, update: update } );
1169
+ res.sendSuccess( `Successfully update in ${inputData.moduleType} ` );
1170
+
1171
+ break;
1172
+
1173
+ case 'left-in-middle':
1174
+ let leftquery ={
1175
+ _id: inputData.auditId,
1176
+ storeId: inputData.storeId,
1177
+ fileDate: inputData.fileDate,
1178
+ question: inputData.question,
1179
+ moduleType: inputData.moduleType,
1180
+ };
1181
+ let leftupdate={
1182
+ auditStatus: 'completed',
1183
+ answer: inputData.answer,
1184
+ };
1185
+ let updatDataleft= await updateOneBinaryAuditModel( leftquery, leftupdate );
1186
+ if ( updatDataleft.modifiedCount==0 ) {
1187
+ return res.sendError( updatDataleft, 204 );
1188
+ }
1189
+ logger.info( 'update in binary data', { query: leftquery, update: leftupdate } );
1190
+ res.sendSuccess( `Successfully update in ${inputData.moduleType} ` );
1191
+
1192
+ break;
1193
+
1194
+ case 'camera-angle-change':
1195
+ let cameraquery ={
1196
+ _id: inputData.auditId,
1197
+ storeId: inputData.storeId,
1198
+ fileDate: inputData.fileDate,
1199
+ streamName: inputData.streamName,
1200
+ moduleType: inputData.moduleType,
1201
+ };
1202
+ let cameraupdate={
1203
+ auditStatus: 'completed',
1204
+ answer: inputData.answer,
1205
+ };
1206
+ let updatDatacamera= await updateOneBinaryAuditModel( cameraquery, cameraupdate );
1207
+ if ( updatDatacamera.modifiedCount==0 ) {
1208
+ return res.sendError( updatDatacamera, 204 );
1209
+ }
1210
+ logger.info( 'update in binary data', { query: cameraquery, update: cameraupdate } );
1211
+ res.sendSuccess( `Successfully update in ${inputData.moduleType} ` );
1212
+
1213
+ break;
1214
+
1215
+ default:
1216
+ res.sendError( 'no data', 204 );
1217
+ break
1218
+ ;
1219
+ }
1220
+ } catch ( error ) {
1221
+ const err = error.mesage || 'Internal Server Error';
1222
+ logger.error( { error: error, data: req.body, function: 'saveBinary' } );
1223
+ return res.sendError( err, 500 );
1224
+ }
1225
+ }
1226
+
1227
+ export async function userAuditHistory( req, res ) {
1228
+ try {
1229
+ const inputData = req.body;
1230
+ const userId = inputData.userId? new mongoose.Types.ObjectId( inputData.userId ) : req.user._id;
1231
+ const limit = inputData.limit || 10;
1232
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
1233
+ const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
1234
+ const dateRangeCreatedAt = await getDateWithCustomizeTime( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
1235
+ let filter = [
1236
+ { userId: { $eq: userId } },
1237
+ { moduleType: { $eq: inputData.moduleType } },
1238
+ ];
1239
+
1240
+ if ( inputData.dateType == 'fileDate' ) {
1241
+ filter.push(
1242
+ { fileDateISO: { $gte: dateRange.start } },
1243
+ { fileDateISO: { $lte: dateRange.end } },
1244
+ );
1245
+ } else {
1246
+ filter.push(
1247
+ { createdAt: { $gte: dateRangeCreatedAt.start } },
1248
+ { createdAt: { $lte: dateRangeCreatedAt.end } },
1249
+ );
1250
+ }
1251
+
1252
+
1253
+ if ( inputData.filterByAuditType?.length > 0 ) {
1254
+ filter.push( {
1255
+ auditType: { $in: inputData.filterByAuditType },
1256
+ } );
1257
+ }
1258
+
1259
+ if ( inputData.filterByStoreId?.length > 0 ) {
1260
+ filter.push( {
1261
+ storeId: { $in: inputData.filterByStoreId },
1262
+ } );
1263
+ }
1264
+
1265
+ if ( inputData.filterByStatus?.length > 0 ) {
1266
+ filter.push( {
1267
+ auditStatus: { $in: inputData.filterByStatus },
1268
+ } );
1269
+ }
1270
+ if ( inputData?.clientId?.length > 0 ) {
1271
+ filter.push( { clientId: { $in: inputData.clientId } } );
1272
+ }
1273
+ const query = [
1274
+ {
1275
+ $match: {
1276
+ $and: filter,
1277
+ },
1278
+ },
1279
+ {
1280
+ $lookup: {
1281
+ from: 'stores',
1282
+ let: { storeId: '$storeId' },
1283
+ pipeline: [
1284
+ {
1285
+ $match: {
1286
+ $expr: {
1287
+ $eq: [ '$storeId', '$$storeId' ],
1288
+ },
1289
+ },
1290
+ },
1291
+ {
1292
+ $project: {
1293
+ storeName: 1,
1294
+ },
1295
+ },
1296
+ ], as: 'store',
1297
+ },
1298
+ },
1299
+ {
1300
+ $unwind: {
1301
+ path: '$store', preserveNullAndEmptyArrays: true,
1302
+ },
1303
+ },
1304
+ {
1305
+ $lookup: {
1306
+ from: 'clients',
1307
+ let: { clientId: '$clientId' },
1308
+ pipeline: [
1309
+ {
1310
+ $match: {
1311
+ $expr: {
1312
+ $eq: [ '$clientId', '$$clientId' ],
1313
+ },
1314
+ },
1315
+ },
1316
+ {
1317
+ $project: {
1318
+ clientName: 1,
1319
+ },
1320
+ },
1321
+ ], as: 'client',
1322
+ },
1323
+
1324
+ },
1325
+ {
1326
+ $unwind: {
1327
+ path: '$client', preserveNullAndEmptyArrays: true,
1328
+ },
1329
+ },
1330
+ {
1331
+ $project: {
1332
+ storeId: 1,
1333
+ userId: 1,
1334
+ tempId: 1,
1335
+ streamName: 1,
1336
+ question: 1,
1337
+ answer: 1,
1338
+ storeName: '$store.storeName',
1339
+ clientId: 1,
1340
+ clientName: '$client.clientName',
1341
+ fileDate: 1,
1342
+ auditType: 1,
1343
+ zoneName: 1,
1344
+ moduleType: 1,
1345
+ beforeCount: 1,
1346
+ afterCount: 1,
1347
+ accuracy: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, { $round: [
1348
+ {
1349
+ $multiply: [ { $divide: [ '$afterCount', '$beforeCount' ] }, 100 ],
1350
+ }, 1,
1351
+ ],
1352
+ }, null ] },
1353
+
1354
+ startTime: {
1355
+ $dateToString: {
1356
+ format: '%Y-%m-%d %H:%M:%S',
1357
+ date: '$startTime',
1358
+ timezone: 'Asia/Kolkata',
1359
+ },
1360
+ },
1361
+ endTime: {
1362
+ $dateToString: {
1363
+ format: '%Y-%m-%d %H:%M:%S',
1364
+ date: '$endTime',
1365
+ timezone: 'Asia/Kolkata',
1366
+ },
1367
+ },
1368
+ // timeSpent: { $round: [
1369
+ // { $divide: [ '$timeSpent', 60 ] }, 2,
1370
+ // ] },
1371
+ timeSpent: {
1372
+
1373
+ $cond: [
1374
+ { $lt: [ '$timeSpent', 60 ] }, // Case 1: If less than 60 seconds
1375
+ { $concat: [ { $toString: '$timeSpent' }, ' sec' ] },
1376
+ {
1377
+ $cond: [
1378
+ { $lt: [ '$timeSpent', 3600 ] }, // Case 2: If less than 60 minutes (3600 seconds)
1379
+ {
1380
+ $concat: [
1381
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 60 ] }, 1 ] } }, // Convert to minutes
1382
+ ' min',
1383
+ ],
1384
+ },
1385
+ {
1386
+ $concat: [
1387
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 3600 ] }, 1 ] } }, // Convert to hours
1388
+ ' hr',
1389
+ ],
1390
+ },
1391
+ ],
1392
+ },
1393
+ ],
1394
+
1395
+ },
1396
+ auditStatus: 1,
1397
+ createdAt: 1,
1398
+ },
1399
+ },
1400
+ ];
1401
+
1402
+ if ( inputData.searchValue && inputData.searchValue!== '' ) {
1403
+ query.push( {
1404
+ $match: {
1405
+ $or: [
1406
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
1407
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
1408
+ { storeId: { $regex: inputData.searchValue, $options: 'i' } },
1409
+ { storeName: { $regex: inputData.searchValue, $options: 'i' } },
1410
+ { streamName: { $regex: inputData.searchValue, $options: 'i' } },
1411
+ { moduleType: { $regex: inputData.searchValue, $options: 'i' } },
1412
+ { auditType: { $regex: inputData.searchValue, $options: 'i' } },
1413
+ { auditStatus: { $regex: inputData.searchValue, $options: 'i' } },
1414
+ { tempId: { $regex: inputData.searchValue, $options: 'i' } },
1415
+ { question: { $regex: inputData.searchValue, $options: 'i' } },
1416
+ ],
1417
+
1418
+ },
1419
+ } );
1420
+ }
1421
+
1422
+ if ( inputData.sortColumnName ) {
1423
+ const sortBy = inputData.sortBy || -1;
1424
+ const sortColumn = inputData.sortColumnName;
1425
+ query.push( {
1426
+ $sort: { [sortColumn]: sortBy },
1427
+ },
1428
+ );
1429
+ }
1430
+ const count = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )? await aggregateUserEmpDetection( query ) : await aggregateBinaryAudit( query );
1431
+ if ( count.length == 0 ) {
1432
+ return res.sendError( 'No Data Found', 204 );
1433
+ }
1434
+
1435
+ if ( inputData.isExport ) {
1436
+ const chunkedMappingData = await chunkArray( count, 10 );
1437
+ const promises = chunkedMappingData.map( async ( chunk ) => {
1438
+ const exportData = [];
1439
+ chunk.forEach( ( element ) => {
1440
+ exportData.push( {
1441
+ 'File Date': element.fileDate,
1442
+ 'Brand ID': element.clientId,
1443
+ 'Brand Name': element.clientName,
1444
+ 'Store ID': element.storeId,
1445
+ 'Store Name': element.storeName,
1446
+ 'Stream Name': element.moduleType == 'camera-angle-change'? element.zoneName : '',
1447
+ 'Audit Type': element.auditType,
1448
+ 'Product Type': element.moduleType,
1449
+ 'Before Count': element.beforeCount,
1450
+ 'After Count': element.afterCount || '',
1451
+ 'Accuracy': element.accuracy || '',
1452
+ 'Start Time': element.startTime,
1453
+ 'End Time': element.endTime || '',
1454
+ 'Time Spent': element.timeSpent,
1455
+ 'Audit Status': element.auditStatus,
1456
+ 'Audited Date': dayjs( element.createdAt ).format( 'YYYY,MMM D' ),
1457
+
1458
+ } );
1459
+ } );
1460
+ return exportData;
1461
+ } );
1462
+ const mappedArrays = await Promise.all( promises );
1463
+ mappedArrays.flat();
1464
+ logger.info( { mappedArrays: mappedArrays[0] } );
1465
+ await download( mappedArrays[0], res );
1466
+ return;
1467
+ }
1468
+
1469
+ query.push( {
1470
+ $skip: offset,
1471
+ },
1472
+ {
1473
+ $limit: limit,
1474
+ } );
1475
+ const result = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )? await aggregateUserEmpDetection( query ) : await aggregateBinaryAudit( query );
1476
+
1477
+ return res.sendSuccess( { result: result, count: count.length } );
1478
+ } catch ( error ) {
1479
+ const err = error.message || 'Internal Server Error';
1480
+ logger.info( { error: error, message: req.body, function: 'userAuditHistory' } );
1481
+ return res.sendError( err, 500 );
1482
+ }
1483
+ }
1484
+
1485
+ export async function clientMetrics( req, res ) {
1486
+ try {
1487
+ const inputData = req.body;
1488
+ const limit = inputData.limit || 10;
1489
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
1490
+ const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
1491
+ let filter = [
1492
+ { fileDateISO: { $gte: dateRange.start } },
1493
+ { fileDateISO: { $lte: dateRange.end } },
1494
+ { moduleType: { $eq: inputData.moduleType } },
1495
+ ];
1496
+
1497
+ let storeAuditFilter = [
1498
+ { $eq: [ '$clientId', '$$clientId' ] },
1499
+ { $eq: [ '$fileDate', '$$fileDate' ] },
1500
+ { $eq: [ '$moduleType', inputData.moduleType ] },
1501
+
1502
+ ];
1503
+
1504
+ if ( inputData?.filterByClient?.length> 0 ) {
1505
+ filter.push(
1506
+ { clientId: { $in: inputData.filterByClient } },
1507
+ );
1508
+ }
1509
+ const query = [
1510
+ {
1511
+ $match: {
1512
+ $and: filter,
1513
+ },
1514
+ },
1515
+ {
1516
+ $group: {
1517
+ _id: { clientId: '$clientId', fileDate: '$fileDate', moduleType: '$moduleType' },
1518
+ fileDate: { $last: '$fileDate' },
1519
+ clientId: { $last: '$clientId' },
1520
+ clientName: { $last: '$clientName' },
1521
+ moduleType: { $last: '$moduleType' },
1522
+ installedStore: { $last: '$installedStore' },
1523
+ queueName: { $last: '$queueName' },
1524
+ totalFilesCount: { $sum: 1 },
1525
+ },
1526
+ },
1527
+ {
1528
+ $project: {
1529
+ fileDate: 1,
1530
+ clientId: 1,
1531
+ clientName: 1,
1532
+ queueName: 1,
1533
+ installedStore: 1,
1534
+ totalFilesCount: 1,
1535
+ moduleType: 1,
1536
+ notAssignedCount: { $ifNull: [ 0, 0 ] },
1537
+ clientStatus: { $ifNull: [ '', '' ] },
1538
+ },
1539
+ },
1540
+ {
1541
+ $lookup: {
1542
+ 'from': 'binaryAudit',
1543
+ 'let': { clientId: '$clientId', fileDate: '$fileDate', totalAuditFiles: '$totalFilesCount' },
1544
+ 'pipeline': [
1545
+
1546
+ {
1547
+ $match: {
1548
+ $expr: {
1549
+ $and: storeAuditFilter,
1550
+ },
1551
+ },
1552
+ },
1553
+ {
1554
+ $project: {
1555
+ completedStores: { $cond: [ { $eq: [ '$status', 'completed' ] }, 1, 0 ] },
1556
+ notAssignedStores: { $cond: [ { $eq: [ '$status', 'not_assign' ] }, 1, 0 ] },
1557
+ inprogressStores: { $cond: [ { $in: [ '$status', [ 'inprogress', 'drafted', 'assigned' ] ] }, 1, 0 ] },
1558
+ },
1559
+ },
1560
+ {
1561
+ $group: {
1562
+ _id: { clientId: '$clientId', fileDate: '$fileDate' },
1563
+ completedStores: { $sum: '$completedStores' },
1564
+ inprogressStores: { $sum: '$inprogressStores' },
1565
+ notAssignedStores: { $sum: '$notAssignedStores' },
1566
+ fileCount: { $sum: 1 },
1567
+ },
1568
+ },
1569
+ {
1570
+ $project: {
1571
+ _id: 0,
1572
+ completedStores: 1,
1573
+ inprogressStores: 1,
1574
+ notAssignedStores: 1,
1575
+ fileCount: { $ifNull: [ '$fileCount', 0 ] },
1576
+ completionPercentage: { $ifNull: [
1577
+ {
1578
+ $round: [ {
1579
+ $multiply: [
1580
+ 100, {
1581
+ $divide: [
1582
+ '$completedStores', '$$totalAuditFiles',
1583
+ ],
1584
+ },
1585
+ ],
1586
+ }, 1 ],
1587
+
1588
+ },
1589
+ 0,
1590
+ ],
1591
+ },
1592
+ },
1593
+ },
1594
+ ], 'as': 'binaryAudit',
1595
+ },
1596
+ },
1597
+ {
1598
+ $unwind: {
1599
+ path: '$binaryAudit',
1600
+ preserveNullAndEmptyArrays: true,
1601
+ },
1602
+ },
1603
+ {
1604
+ $project: {
1605
+ _id: 0,
1606
+ fileDate: 1,
1607
+ clientId: 1,
1608
+ clientName: 1,
1609
+ totalFilesCount: 1,
1610
+ moduleType: 1,
1611
+ installedStore: 1,
1612
+ // notAssignedStores: '$binaryAudit.notAssignedStores',
1613
+ inprogressStoresCount: '$binaryAudit.inprogressStores',
1614
+ // fileCount: '$binaryAudit.fileCount',
1615
+ notAssignedCount: { $subtract: [ '$totalFilesCount', { $subtract: [ { $ifNull: [ '$binaryAudit.fileCount', 0 ] }, { $ifNull: [ '$binaryAudit.notAssignedStores', 0 ] } ] } ] },
1616
+ clientStatus: { $cond: [ { $eq: [ { $ifNull: [ '$binaryAudit.fileCount', 0 ] }, 0 ] }, 'not-taken', { $cond: [ { $gte: [ '$binaryAudit.completedStores', '$totalFilesCount' ] }, 'completed', 'pending' ] } ] }, // { $cond: [ { $or: [ { $gt: [ '$notAssignedCount', 0 ] }, { $gt: [ '$binaryAudit.notAssignedStores', 0 ] } ] }
1617
+ completedStores: '$binaryAudit.completedStores',
1618
+ completionPercentage: '$binaryAudit.completionPercentage',
1619
+
1620
+ },
1621
+ },
1622
+ ];
1623
+
1624
+ if ( inputData?.filterByStatus?.length> 0 ) {
1625
+ query.push(
1626
+ {
1627
+ $match: { clientStatus: { $in: inputData.filterByStatus } },
1628
+ },
1629
+
1630
+ );
1631
+ }
1632
+
1633
+ if ( inputData.searchValue && inputData.searchValue!== '' ) {
1634
+ query.push( {
1635
+ $match: {
1636
+ $or: [
1637
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
1638
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
1639
+ { moduleType: { $regex: inputData.searchValue, $options: 'i' } },
1640
+ { clientStatus: { $regex: inputData.searchValue, $options: 'i' } },
1641
+ ],
1642
+
1643
+ },
1644
+ } );
1645
+ }
1646
+ if ( inputData.sortColumnName ) {
1647
+ const sortBy = inputData.sortBy || -1;
1648
+ const sortColumn = inputData.sortColumnName;
1649
+ query.push( {
1650
+ $sort: { [sortColumn]: sortBy },
1651
+ },
1652
+ );
1653
+ }
1654
+
1655
+ const count = await aggregateTraxAuditData( query );
1656
+ logger.info( { count: count } );
1657
+ if ( count.length == 0 ) {
1658
+ return res.sendError( 'No Data Found', 204 );
1659
+ }
1660
+
1661
+ query.push(
1662
+ { $skip: offset },
1663
+ { $limit: limit },
1664
+ );
1665
+ if ( inputData.isExport ) {
1666
+ query.push(
1667
+ {
1668
+ $project: {
1669
+ '_id': 0,
1670
+ 'File Date': '$fileDate',
1671
+ 'Client Name': '$clientName',
1672
+ 'Client Id': '$clientId',
1673
+ 'Product Type': '$moduleType',
1674
+ 'Completed Percentage': '$completionPercentage',
1675
+ 'Installed Stores': '$installedStore',
1676
+ 'Audit Stores': '$totalFilesCount',
1677
+ 'Inprogress Stores': '$inprogressStoresCount',
1678
+ 'Not Assigned': '$notAssignedCount',
1679
+ // 'Not Assigned Stores': '$notAssignedStores',
1680
+ 'Completed': '$completedStores',
1681
+ 'Status': '$clientStatus',
1682
+ },
1683
+ },
1684
+ );
1685
+ }
1686
+
1687
+ logger.info( { query: query } );
1688
+ const result = await aggregateTraxAuditData( query );
1689
+ if ( result.length == 0 ) {
1690
+ return res.sendError( 'No Data Found', 204 );
1691
+ }
1692
+ if ( inputData.isExport ) {
1693
+ await download( result, res );
1694
+ return;
1695
+ }
1696
+ return res.sendSuccess( { result: result, count: count.length } );
1697
+ } catch ( error ) {
1698
+ const err = error.error || 'Internal Server Error';
1699
+ logger.info( { error: error, message: req.body, function: 'clientMetrics' } );
1700
+ return res.sendError( err, 500 );
1701
+ }
1702
+ }
1703
+
1704
+ export async function storeMetrics( req, res ) {
1705
+ try {
1706
+ const inputData = req.body;
1707
+ const limit = inputData.limit || 10;
1708
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
1709
+ const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
1710
+ let filter = [
1711
+ { fileDateISO: { $gte: dateRange.start } },
1712
+ { fileDateISO: { $lte: dateRange.end } },
1713
+ { moduleType: { $eq: inputData.moduleType } },
1714
+
1715
+ ];
1716
+ if ( inputData.filterByClientId && inputData.filterByClientId?.length > 0 ) {
1717
+ filter.push( { clientId: { $in: inputData.filterByClientId } } );
1718
+ }
1719
+
1720
+ if ( inputData.filterByStoreId && inputData.filterByStoreId?.length > 0 ) {
1721
+ filter.push( { storeId: { $in: inputData.filterByStoreId } } );
1722
+ }
1723
+
1724
+ if ( inputData.filterByStatus && inputData.filterByStatus?.length > 0 ) {
1725
+ filter.push( { status: { $in: inputData.filterByStatus } } );
1726
+ }
1727
+
1728
+ if ( inputData.filterByAuditType && inputData.filterByAuditType?.length > 0 ) {
1729
+ filter.push( { auditType: { $in: inputData.filterByAuditType } } );
1730
+ }
1731
+
1732
+
1733
+ const query = [
1734
+ {
1735
+ $match: {
1736
+ $and: filter,
1737
+ },
1738
+ },
1739
+
1740
+ {
1741
+ $lookup: {
1742
+ from: 'stores',
1743
+ let: { storeId: '$storeId' },
1744
+ pipeline: [
1745
+ {
1746
+ $match: {
1747
+ $expr: {
1748
+ $eq: [ '$storeId', '$$storeId' ],
1749
+ },
1750
+ },
1751
+ },
1752
+ {
1753
+ $project: {
1754
+ _id: 0,
1755
+ storeName: 1,
1756
+ },
1757
+ },
1758
+ ], as: 'stores',
1759
+ },
1760
+ },
1761
+ {
1762
+ $unwind: {
1763
+ path: '$stores',
1764
+ preserveNullAndEmptyArrays: true,
1765
+ },
1766
+ },
1767
+ {
1768
+ $project: {
1769
+ _id: 0,
1770
+ fileDate: 1,
1771
+ storeId: 1,
1772
+ storeName: '$stores.storeName',
1773
+ clientName: '',
1774
+ clientId: 1,
1775
+ moduleType: 1,
1776
+ tempId: 1,
1777
+ streamName: 1,
1778
+ question: 1,
1779
+ answer: 1,
1780
+ // userId: {
1781
+ // $arrayElemAt: [ '$userId', { $subtract: [ { $size: '$userId' }, 1 ] } ], //need to check if no data
1782
+ // },
1783
+ auditType: 1,
1784
+ beforeCount: 1,
1785
+ afterCount: { $ifNull: [ '$afterCount', null ] },
1786
+ accuracy: { $cond: [ { $eq: [ '$status', 'completed' ] }, { $round: [
1787
+ {
1788
+ $multiply: [ { $divide: [ '$afterCount', '$beforeCount' ] }, 100 ],
1789
+ }, 1,
1790
+ ],
1791
+ }, null ] },
1792
+ timeSpent: 1,
1793
+ status: 1,
1794
+ },
1795
+ },
1796
+ {
1797
+ $lookup: {
1798
+ from: 'users',
1799
+ let: { userId: '$userId' },
1800
+ pipeline: [
1801
+ {
1802
+ $match: {
1803
+ $expr: {
1804
+ $eq: [ '$_id', '$$userId' ],
1805
+ },
1806
+ },
1807
+ },
1808
+ {
1809
+ $project: {
1810
+ _id: 0,
1811
+ userName: 1,
1812
+ userEmail: '$email',
1813
+ },
1814
+ },
1815
+ ], as: 'users',
1816
+ },
1817
+ },
1818
+ {
1819
+ $unwind: {
1820
+ path: '$users',
1821
+ preserveNullAndEmptyArrays: true,
1822
+ },
1823
+ },
1824
+ {
1825
+ $lookup: {
1826
+ from: 'clients',
1827
+ let: { clientId: '$clientId' },
1828
+ pipeline: [
1829
+ {
1830
+ $match: {
1831
+ $expr: {
1832
+ $eq: [ '$clientId', '$$clientId' ],
1833
+ },
1834
+ },
1835
+ },
1836
+ {
1837
+ $project: {
1838
+ _id: 0,
1839
+ clientName: 1,
1840
+ },
1841
+ },
1842
+ ], as: 'client',
1843
+ },
1844
+ },
1845
+ {
1846
+ $unwind: {
1847
+ path: '$client',
1848
+ preserveNullAndEmptyArrays: true,
1849
+ },
1850
+ },
1851
+ {
1852
+ $project: {
1853
+ _id: 0,
1854
+ fileDate: 1,
1855
+ storeId: 1,
1856
+ storeName: 1,
1857
+ userName: '$users.userName',
1858
+ userEmail: '$users.userEmail',
1859
+ clientId: 1,
1860
+ clientName: '$client.clientName',
1861
+ tempId: 1,
1862
+ streamName: 1,
1863
+ question: 1,
1864
+ answer: 1,
1865
+ moduleType: 1,
1866
+ auditType: 1,
1867
+ beforeCount: 1,
1868
+ afterCount: { $ifNull: [ '$afterCount', null ] },
1869
+ accuracy: { $cond: [ { $eq: [ '$status', 'completed' ] }, { $round: [
1870
+ {
1871
+ $multiply: [ { $divide: [ '$afterCount', '$beforeCount' ] }, 100 ],
1872
+ }, 1,
1873
+ ],
1874
+ }, null ] },
1875
+ timeSpent: {
1876
+
1877
+ $cond: [
1878
+ { $lt: [ '$timeSpent', 60 ] }, // Case 1: If less than 60 seconds
1879
+ { $concat: [ { $toString: '$timeSpent' }, ' sec' ] },
1880
+ {
1881
+ $cond: [
1882
+ { $lt: [ '$timeSpent', 3600 ] }, // Case 2: If less than 60 minutes (3600 seconds)
1883
+ {
1884
+ $concat: [
1885
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 60 ] }, 1 ] } }, // Convert to minutes
1886
+ ' min',
1887
+ ],
1888
+ },
1889
+ {
1890
+ $concat: [
1891
+ { $toString: { $round: [ { $divide: [ '$timeSpent', 3600 ] }, 1 ] } }, // Convert to hours
1892
+ ' hr',
1893
+ ],
1894
+ },
1895
+ ],
1896
+ },
1897
+ ],
1898
+
1899
+ },
1900
+ status: 1,
1901
+ },
1902
+ },
1903
+
1904
+ ];
1905
+
1906
+ if ( inputData.searchValue && inputData.searchValue !== '' ) {
1907
+ query.push( {
1908
+ $match: {
1909
+ $or: [
1910
+ { clientId: { $regex: inputData.searchValue, $options: 'i' } },
1911
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
1912
+ { storeId: { $regex: inputData.searchValue, $options: 'i' } },
1913
+ { storeName: { $regex: inputData.searchValue, $options: 'i' } },
1914
+ { userName: { $regex: inputData.searchValue, $options: 'i' } },
1915
+ { status: { $regex: inputData.searchValue, $options: 'i' } },
1916
+ { auditType: { $regex: inputData.searchValue, $options: 'i' } },
1917
+ { moduleType: { $regex: inputData.searchValue, $options: 'i' } },
1918
+ { tempId: { $regex: inputData.searchValue, $options: 'i' } },
1919
+ { question: { $regex: inputData.searchValue, $options: 'i' } },
1920
+ { answer: { $regex: inputData.searchValue, $options: 'i' } },
1921
+ ],
1922
+ },
1923
+ } );
1924
+ }
1925
+
1926
+ if ( inputData.sortColumnName ) {
1927
+ const sortBy = inputData.sortBy || -1;
1928
+ const sortColumn = inputData.sortColumnName;
1929
+ query.push( {
1930
+ $sort: { [sortColumn]: sortBy },
1931
+ },
1932
+ );
1933
+ }
1934
+ const count = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateStoreEmpDetection( query ) : await aggregateBinaryAudit( query );
1935
+
1936
+ if ( count.length == 0 ) {
1937
+ return res.sendError( 'No data Found', 204 );
1938
+ }
1939
+ if ( !inputData.isExport ) {
1940
+ query.push( {
1941
+ $skip: offset,
1942
+ },
1943
+ {
1944
+ $limit: limit,
1945
+ } );
1946
+ } else {
1947
+ query.push( { $limit: 10000 } );
1948
+ }
1949
+ const result = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateUserEmpDetection( query ) : await aggregateBinaryAudit( query );
1950
+ if ( inputData.isExport ) {
1951
+ const exportdata = [];
1952
+ result.forEach( ( element ) => {
1953
+ if ( element.moduleType == 'zone' ) {
1954
+ exportdata.push( {
1955
+ 'File Date': element.fileDate,
1956
+ 'Store ID': element.storeId,
1957
+ 'Store Name': element.storeName,
1958
+ 'Client Id': element.clientId,
1959
+ 'Client Name': element.clientName,
1960
+ 'Customer Id': element.tempId,
1961
+ 'Question': element.question,
1962
+ 'Answer': element.answer,
1963
+ 'User Name': element.userName,
1964
+ 'User Email': element.userEmail,
1965
+ 'Audit Type': element.auditType,
1966
+ 'Accuracy': element.accuracy,
1967
+ 'Time Spent': element.timeSpent,
1968
+ 'Status': element.status,
1969
+ } );
1970
+ } else {
1971
+ exportdata.push( {
1972
+ 'File Date': element.fileDate,
1973
+ 'Store ID': element.storeId,
1974
+ 'Store Name': element.storeName,
1975
+ 'Client Id': element.clientId,
1976
+ 'Client Name': element.clientName,
1977
+ 'User Name': element.userName,
1978
+ 'Customer Id': element.tempId,
1979
+ 'Question': element.question,
1980
+ 'Answer': element.answer,
1981
+ 'User Email': element.userEmail,
1982
+ 'Audit Type': element.auditType,
1983
+ 'Accuracy': element.accuracy,
1984
+ 'Time Spent': element.timeSpent,
1985
+ 'Status': element.status,
1986
+ } );
1987
+ }
1988
+ } );
1989
+ await download( exportdata, res );
1990
+ return;
1991
+ }
1992
+ return res.sendSuccess( { result: result, count: count.length } );
1993
+ } catch ( error ) {
1994
+ const err = error.message || 'Internal Server Error';
1995
+ logger.error( { error: error, data: req.body, function: 'storeMetrics' } );
1996
+ return res.sendError( err, 500 );
1997
+ }
1998
+ }
1999
+
2000
+ export async function userMetrics( req, res ) {
2001
+ try {
2002
+ const inputData = req.body;
2003
+ const limit = inputData.limit || 10;
2004
+ const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
2005
+ const dateRange = await getUTC( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
2006
+ const dateRangeUpdatedAt = await getDateWithCustomizeTime( new Date( inputData.fromDate ), new Date( inputData.toDate ) );
2007
+ let filter = [
2008
+ { moduleType: { $eq: inputData.moduleType } },
2009
+ ];
2010
+ if ( inputData.dateType == 'auditedDate' ) {
2011
+ filter.push(
2012
+ { updatedAt: { $gte: dateRangeUpdatedAt.start } },
2013
+ { updatedAt: { $lte: dateRangeUpdatedAt.end } },
2014
+
2015
+ );
2016
+ } else {
2017
+ filter.push(
2018
+ { fileDateISO: { $gte: dateRange.start } },
2019
+ { fileDateISO: { $lte: dateRange.end } },
2020
+
2021
+ );
2022
+ }
2023
+ if ( inputData?.filterByStatus?.length> 0 ) {
2024
+ filter.push(
2025
+ { auditStatus: { $in: inputData.filterByStatus } },
2026
+ );
2027
+ }
2028
+ if ( inputData?.filterByUser?.length> 0 ) {
2029
+ const temp = inputData.filterByUser.map( ( item ) => new mongoose.Types.ObjectId( item ) );
2030
+ logger.info( { temp: temp } );
2031
+ filter.push(
2032
+ { userId: { $in: temp } },
2033
+ );
2034
+ }
2035
+
2036
+ const query = [
2037
+ {
2038
+ $match: {
2039
+ $and: filter,
2040
+ },
2041
+ },
2042
+ {
2043
+ $project: {
2044
+ userId: 1,
2045
+ fileDate: 1,
2046
+ startTime: 1,
2047
+ endTime: 1,
2048
+ streamName: 1,
2049
+ tempId: 1,
2050
+ question: 1,
2051
+ answer: 1,
2052
+ totalCompletedFiles: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, 1, 0 ] },
2053
+ beforeCount: 1,
2054
+ afterCount: { $cond: [ { $eq: [ '$auditStatus', 'completed' ] }, '$afterCount', 0 ] },
2055
+ },
2056
+ },
2057
+ {
2058
+ $group: {
2059
+ _id: { userId: '$userId', fileDate: '$fileDate' },
2060
+ userId: { $first: '$userId' },
2061
+ fileDate: { $first: '$fileDate' },
2062
+ totalCompletedFiles: { $sum: '$totalCompletedFiles' },
2063
+ beforeCount: { $sum: '$beforeCount' },
2064
+ afterCount: { $sum: '$afterCount' },
2065
+ checkIntime: { $first: '$startTime' },
2066
+ checkOutTime: { $last: '$endTime' },
2067
+ streamName: { $last: '$streamName' },
2068
+ tempId: { $last: '$tempId' },
2069
+ question: { $last: '$question' },
2070
+ answer: { $last: '$answer' },
2071
+
2072
+ },
2073
+ },
2074
+ {
2075
+ $project: {
2076
+ _id: 0,
2077
+ userId: 1,
2078
+ fileDate: 1,
2079
+ totalCompletedFiles: 1,
2080
+ beforeCount: 1,
2081
+ afterCount: 1,
2082
+ streamName: 1,
2083
+ tempId: 1,
2084
+ question: 1,
2085
+ answer: 1,
2086
+ mappingPerc: { $round: [
2087
+ { $multiply: [
2088
+ { $divide: [ '$afterCount', '$beforeCount' ] }, 100,
2089
+ ] }, 2,
2090
+ ] },
2091
+ checkIntime: 1,
2092
+ checkOutTime: 1,
2093
+ differenceInSeconds: {
2094
+ $divide: [ { $subtract: [ '$checkOutTime', '$checkIntime' ] }, 1000 ],
2095
+ },
2096
+ },
2097
+ },
2098
+ {
2099
+ $project: {
2100
+ userId: 1,
2101
+ fileDate: 1,
2102
+ totalCompletedFiles: 1,
2103
+ beforeCount: 1,
2104
+ afterCount: 1,
2105
+ checkIntime: 1,
2106
+ checkOutTime: 1,
2107
+ mappingPerc: 1,
2108
+ streamName: 1,
2109
+ tempId: 1,
2110
+ question: 1,
2111
+ answer: 1,
2112
+ hours: { $floor: { $divide: [ '$differenceInSeconds', 3600 ] } },
2113
+ minutes: { $floor: { $divide: [ { $mod: [ '$differenceInSeconds', 3600 ] }, 60 ] } },
2114
+ seconds: { $mod: [ '$differenceInSeconds', 60 ] },
2115
+ },
2116
+ },
2117
+ {
2118
+ $project: {
2119
+ event: 1,
2120
+ userId: 1,
2121
+ fileDate: 1,
2122
+ totalCompletedFiles: 1,
2123
+ beforeCount: 1,
2124
+ afterCount: 1,
2125
+ checkIntime: 1,
2126
+ checkOutTime: 1,
2127
+ mappingPerc: 1,
2128
+ streamName: 1,
2129
+ tempId: 1,
2130
+ question: 1,
2131
+ answer: 1,
2132
+ hours: {
2133
+ $cond: {
2134
+ if: { $lt: [ '$hours', 10 ] },
2135
+ then: { $concat: [ '0', { $toString: { $round: [ '$hours', 0 ] } } ] },
2136
+ else: { $toString: { $round: [ '$hours', 0 ] } },
2137
+ },
2138
+ },
2139
+ minutes: {
2140
+ $cond: {
2141
+ if: { $lt: [ '$minutes', 10 ] },
2142
+ then: { $concat: [ '0', { $toString: { $round: [ '$minutes', 0 ] } } ] },
2143
+ else: { $toString: { $round: [ '$minutes', 0 ] } },
2144
+ },
2145
+ },
2146
+ seconds: {
2147
+ $cond: {
2148
+ if: { $lt: [ '$seconds', 10 ] },
2149
+ then: { $concat: [ '0', { $toString: { $round: [ '$seconds', 0 ] } } ] },
2150
+ else: { $toString: { $round: [ '$seconds', 0 ] } },
2151
+ },
2152
+ },
2153
+
2154
+ },
2155
+ },
2156
+ {
2157
+ $project: {
2158
+ event: 1,
2159
+ userId: 1,
2160
+ fileDate: 1,
2161
+ totalCompletedFiles: 1,
2162
+ beforeCount: 1,
2163
+ afterCount: 1,
2164
+ checkIntime: 1,
2165
+ zoneName: 1,
2166
+ checkOutTime: 1,
2167
+ mappingPerc: 1,
2168
+ streamName: 1,
2169
+ tempId: 1,
2170
+ question: 1,
2171
+ answer: 1,
2172
+ workingHours: {
2173
+ $concat: [ '$hours', ':', '$minutes', ':', '$seconds' ],
2174
+ },
2175
+ },
2176
+ },
2177
+ {
2178
+ $lookup: {
2179
+ from: 'users',
2180
+ let: { userId: '$userId' },
2181
+ pipeline: [
2182
+ {
2183
+ $match: {
2184
+ $expr: {
2185
+ $eq: [ '$_id', '$$userId' ],
2186
+ },
2187
+ },
2188
+ },
2189
+ {
2190
+ $project: {
2191
+ _id: 0,
2192
+ userName: 1,
2193
+ },
2194
+ },
2195
+ ], as: 'userInfo',
2196
+ },
2197
+ },
2198
+ {
2199
+ $unwind: {
2200
+ path: '$userInfo', preserveNullAndEmptyArrays: true,
2201
+ },
2202
+ },
2203
+ {
2204
+ $project: {
2205
+ event: 1,
2206
+ userName: '$userInfo.userName',
2207
+ userId: 1,
2208
+ fileDate: 1,
2209
+ totalCompletedFiles: 1,
2210
+ beforeCount: 1,
2211
+ afterCount: 1,
2212
+ mappingPerc: 1,
2213
+ streamName: 1,
2214
+ tempId: 1,
2215
+ question: 1,
2216
+ answer: 1,
2217
+ checkIntime: {
2218
+ $dateToString: {
2219
+ format: '%Y-%m-%d %H:%M:%S',
2220
+ date: '$checkIntime',
2221
+ timezone: 'Asia/Kolkata',
2222
+ },
2223
+ },
2224
+ checkOutTime: {
2225
+ $dateToString: {
2226
+ format: '%Y-%m-%d %H:%M:%S',
2227
+ date: '$checkOutTime',
2228
+ timezone: 'Asia/Kolkata',
2229
+ },
2230
+ },
2231
+ workingHours: 1,
2232
+ },
2233
+ },
2234
+ ];
2235
+ if ( inputData.searchValue && inputData.searchValue!== '' ) {
2236
+ query.push( {
2237
+ $match: {
2238
+ $or: [
2239
+ { userName: { $regex: inputData.searchValue, $options: 'i' } },
2240
+ { clientName: { $regex: inputData.searchValue, $options: 'i' } },
2241
+ ],
2242
+
2243
+ },
2244
+ } );
2245
+ }
2246
+ if ( inputData.sortColumnName ) {
2247
+ const sortBy = inputData.sortBy || -1;
2248
+ const sortColumn = inputData.sortColumnName;
2249
+ query.push( {
2250
+ $sort: { [sortColumn]: sortBy },
2251
+ },
2252
+ );
2253
+ }
2254
+
2255
+ const count = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateUserEmpDetection( query ) : await aggregateBinaryAudit( query );
2256
+ if ( count.length == 0 ) {
2257
+ return res.sendError( 'No Data Found', 204 );
2258
+ }
2259
+
2260
+ query.push(
2261
+ { $skip: offset },
2262
+ { $limit: limit },
2263
+ );
2264
+ if ( inputData.isExport ) {
2265
+ query.push(
2266
+ {
2267
+ $project: {
2268
+ '_id': 0,
2269
+ 'File Date': '$fileDate',
2270
+ 'User Name': '$userName',
2271
+ 'Total Completed Files Count': '$totalCompletedFiles',
2272
+ 'Total Before Count': '$beforeCount',
2273
+ 'Total After Count': '$afterCount',
2274
+ 'check Intime': '$checkIntime',
2275
+ 'check OutTime': '$checkOutTime',
2276
+ 'working Hours': '$workingHours',
2277
+ 'Mapping Percentage': '$mappingPerc',
2278
+ },
2279
+ },
2280
+ );
2281
+ }
2282
+
2283
+
2284
+ const result = inputData.moduleType.includes( [ 'uniform-detection', 'mobile-detection' ] )?await aggregateUserEmpDetection( query ) : await aggregateBinaryAudit( query );
2285
+ if ( inputData.isExport ) {
2286
+ await download( result, res );
2287
+ return;
2288
+ }
2289
+ return res.sendSuccess( { result: result, count: count.length } );
2290
+ } catch ( error ) {
2291
+ const err = error.error || 'Internal Server Error';
2292
+ logger.info( { error: error, message: req.body, function: 'userMetrics' } );
2293
+ return res.sendError( err, 500 );
2294
+ }
2295
+ }