tango-app-api-trax 3.9.23 → 3.9.25
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 +2 -2
- package/src/controllers/internalTrax.controller.js +114 -73
- package/src/controllers/mobileTrax.controller.js +47 -28
- package/src/controllers/trax.controller.js +2 -1
- package/src/controllers/traxDashboard.controllers.js +63 -54
- package/src/hbs/flag.hbs +1 -1
- package/src/logging/activityLogFlusher.js +59 -0
- package/src/logging/activityLogMiddleware.js +45 -0
- package/src/logging/activityLogStore.js +91 -0
- package/src/logging/compressBatches.js +83 -0
- package/src/logging/config.js +24 -0
- package/src/logging/createLoggableService.js +46 -0
- package/src/logging/logExternalCall.js +37 -0
- package/src/services/app.service.js +20 -14
- package/src/services/approver.service.js +28 -20
- package/src/services/authentication.service.js +12 -6
- package/src/services/camera.service.js +24 -18
- package/src/services/checklist.service.js +36 -31
- package/src/services/checklistAssign.service.js +44 -39
- package/src/services/checklistQuestion.service.js +40 -35
- package/src/services/checklistlog.service.js +40 -35
- package/src/services/clientRequest.service.js +12 -5
- package/src/services/clients.services.js +23 -18
- package/src/services/cluster.service.js +36 -28
- package/src/services/domain.service.js +33 -28
- package/src/services/download.services.js +48 -38
- package/src/services/group.service.js +28 -22
- package/src/services/lenskartEmployeeMapping.service.js +20 -15
- package/src/services/locus.service.js +41 -34
- package/src/services/notification.service.js +40 -31
- package/src/services/otp.service.js +24 -17
- package/src/services/planogram.service.js +12 -5
- package/src/services/processedTaskConfig.service.js +40 -32
- package/src/services/processedTaskList.service.js +36 -30
- package/src/services/processedchecklist.services.js +56 -48
- package/src/services/processedchecklistconfig.services.js +40 -35
- package/src/services/recurringFlagTracker.service.js +40 -33
- package/src/services/runAIFeatures.services.js +36 -31
- package/src/services/runAIRequest.services.js +44 -39
- package/src/services/store.service.js +36 -31
- package/src/services/tagging.service.js +12 -5
- package/src/services/taskConfig.service.js +40 -32
- package/src/services/teams.service.js +41 -30
- package/src/services/ticket.service.js +20 -15
- package/src/services/user.service.js +32 -25
- 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.
|
|
3
|
+
"version": "3.9.25",
|
|
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.
|
|
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",
|
|
@@ -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' );
|
|
@@ -1383,40 +1384,40 @@ export async function insertTimeDelayFlags( req, res ) {
|
|
|
1383
1384
|
let updatedDetails = await processedchecklist.updateMany( { _id: { $in: checklistId } }, { timeFlag: 1 } );
|
|
1384
1385
|
|
|
1385
1386
|
|
|
1386
|
-
let notifyStores = await processedchecklist.find( { _id: { $in: checklistId }, notify: { $exists: false } } );
|
|
1387
|
-
|
|
1388
|
-
if ( notifyStores.length ) {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
}
|
|
1387
|
+
// let notifyStores = await processedchecklist.find( { _id: { $in: checklistId }, notify: { $exists: false } } );
|
|
1388
|
+
|
|
1389
|
+
// if ( notifyStores.length ) {
|
|
1390
|
+
// Promise.all( notifyStores.map( async ( store ) => {
|
|
1391
|
+
// let checklistDetails = await CLconfig.findOne( { _id: store.sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
|
|
1392
|
+
// if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
|
|
1393
|
+
// let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
|
|
1394
|
+
// emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
|
|
1395
|
+
// let data = {
|
|
1396
|
+
// storeName: store.storeName,
|
|
1397
|
+
// flagCount: 1,
|
|
1398
|
+
// checklistName: store.checkListName?.trim(),
|
|
1399
|
+
// submittedBy: '--',
|
|
1400
|
+
// time: '--',
|
|
1401
|
+
// domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
|
|
1402
|
+
// type: 'delay',
|
|
1403
|
+
// };
|
|
1404
|
+
// const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
|
|
1405
|
+
// const htmlContent = handlebars.compile( fileContent );
|
|
1406
|
+
// const html = htmlContent( { data: data } );
|
|
1407
|
+
// emailList.forEach( ( email ) => {
|
|
1408
|
+
// let params = {
|
|
1409
|
+
// toEmail: email,
|
|
1410
|
+
// mailSubject: 'TangoEye | Checklist Flag',
|
|
1411
|
+
// htmlBody: html,
|
|
1412
|
+
// attachment: '',
|
|
1413
|
+
// sourceEmail: JSON.parse( process.env.SES ).adminEmail,
|
|
1414
|
+
// };
|
|
1415
|
+
// sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
1416
|
+
// } );
|
|
1417
|
+
// }
|
|
1418
|
+
// await processedchecklist.updateOne( { _id: store._id }, { notify: true } );
|
|
1419
|
+
// } ) );
|
|
1420
|
+
// }
|
|
1420
1421
|
|
|
1421
1422
|
if ( updatedDetails ) {
|
|
1422
1423
|
return res.sendSuccess( { message: 'Time Delay Updated Successfully' } );
|
|
@@ -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: '
|
|
2754
|
+
logger.error( { functionName: 'countUpdateRunAI', error: e } );
|
|
2707
2755
|
return res.sendError( e, 500 );
|
|
2708
2756
|
}
|
|
2709
2757
|
}
|
|
@@ -3540,47 +3588,40 @@ export async function runAIFlag( req, res ) {
|
|
|
3540
3588
|
],
|
|
3541
3589
|
},
|
|
3542
3590
|
}, { _id: 1, notifyFlags: 1, approver: 1 } );
|
|
3543
|
-
|
|
3591
|
+
console.log( checklistDetails );
|
|
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,
|
|
3595
|
+
let submitDetails = await processedchecklist.find( { sourceCheckList_id: ele._id, date_string: date, $or: [ { timeFlag: { $gt: 0 } }, { questionFlag: { $gt: 0 } }, { runAIFlag: { $gt: 0 } } ] }, { storeName: 1, checkListName: 1, userName: 1, submitTime_string: 1, runAIFlag: 1, questionFlag: 1, checklistStatus: 1 } );
|
|
3596
|
+
console.log( submitDetails );
|
|
3597
|
+
let emailList = ele?.notifyFlags?.notifyType.includes( 'approver' ) ? ele.approver.map( ( approver ) => approver?.value ): [];
|
|
3598
|
+
emailList = [ ...emailList, ...ele?.notifyFlags?.users?.map( ( user ) => user?.value ) ];
|
|
3547
3599
|
await Promise.all( submitDetails.map( ( store ) => {
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
store.
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
} )
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
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
|
-
}
|
|
3600
|
+
let data = {
|
|
3601
|
+
storeName: store.storeName,
|
|
3602
|
+
flagCount: store.timeFlag ? 1 : store?.runAIFlag + store?.questionFlag,
|
|
3603
|
+
runAIFlag: store?.runAIFlag,
|
|
3604
|
+
questionFlag: store?.questionFlag,
|
|
3605
|
+
checklistName: store.checkListName?.trim(),
|
|
3606
|
+
submittedBy: store?.userName,
|
|
3607
|
+
time: store?.submitTime_string ?? '--',
|
|
3608
|
+
domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
|
|
3609
|
+
status: store.checklistStatus,
|
|
3610
|
+
};
|
|
3611
|
+
const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
|
|
3612
|
+
const htmlContent = handlebars.compile( fileContent );
|
|
3613
|
+
const html = htmlContent( { data: data } );
|
|
3614
|
+
emailList.forEach( ( email ) => {
|
|
3615
|
+
console.log( email );
|
|
3616
|
+
let params = {
|
|
3617
|
+
toEmail: email,
|
|
3618
|
+
mailSubject: 'TangoEye | Checklist Flag',
|
|
3619
|
+
htmlBody: html,
|
|
3620
|
+
attachment: '',
|
|
3621
|
+
sourceEmail: JSON.parse( process.env.SES ).adminEmail,
|
|
3622
|
+
};
|
|
3623
|
+
sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
3624
|
+
} );
|
|
3584
3625
|
} ) );
|
|
3585
3626
|
} ) );
|
|
3586
3627
|
}
|
|
@@ -1777,6 +1777,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
|
|
|
1777
1777
|
};
|
|
1778
1778
|
}
|
|
1779
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
|
+
}
|
|
1780
1792
|
}
|
|
1781
1793
|
// qaans[k].descriptivetype = qaAnswers[j].descriptivetype || '';
|
|
1782
1794
|
qaans[k].validationAnswer = requestSection[i].validationAnswer || '';
|
|
@@ -2127,12 +2139,18 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
|
|
|
2127
2139
|
logger.error( { message: requestData.questionAnswers, error: 'QuestionanswersPayload' } );
|
|
2128
2140
|
|
|
2129
2141
|
if ( requestData.submittype == 'submit' ) {
|
|
2142
|
+
requestData.userComplianceCount = 0;
|
|
2130
2143
|
for ( let section of sectionFormat ) {
|
|
2131
2144
|
for ( let question of section.questions ) {
|
|
2132
2145
|
let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
|
|
2133
2146
|
if ( mustValidate && ( !question.userAnswer || !question.userAnswer.length ) ) {
|
|
2134
2147
|
return res.sendError( 'Please Fill All Fields', 400 );
|
|
2135
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
|
+
}
|
|
2136
2154
|
}
|
|
2137
2155
|
}
|
|
2138
2156
|
}
|
|
@@ -2776,6 +2794,7 @@ export async function submitChecklist( req, res ) {
|
|
|
2776
2794
|
updateData.checklistStatus = 'submit';
|
|
2777
2795
|
updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
|
|
2778
2796
|
updateData.submitMobileTime = requestData?.currentTime;
|
|
2797
|
+
updateData.userComplianceCount = requestData?.userComplianceCount;
|
|
2779
2798
|
}
|
|
2780
2799
|
// updateData.questionAnswers.forEach( ( section ) => {
|
|
2781
2800
|
// section.questions.forEach( ( question ) => {
|
|
@@ -2876,34 +2895,34 @@ export async function submitChecklist( req, res ) {
|
|
|
2876
2895
|
userType: req.user.userType,
|
|
2877
2896
|
};
|
|
2878
2897
|
insertOpenSearchData( openSearch.traxActivityLog, inserttraxlogs );
|
|
2879
|
-
if ( updateData.questionFlag ) {
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
}
|
|
2898
|
+
// if ( updateData.questionFlag ) {
|
|
2899
|
+
// let checklistDetails = await checklistService.findOne( { _id: getchecklist[0].sourceCheckList_id }, { notifyFlags: 1, approver: 1 } );
|
|
2900
|
+
// if ( checklistDetails?.notifyFlags?.notifyType?.length ) {
|
|
2901
|
+
// let emailList = checklistDetails?.notifyFlags?.notifyType.includes( 'approver' ) ? checklistDetails.approver.map( ( ele ) => ele?.value ): [];
|
|
2902
|
+
// emailList = [ ...emailList, ...checklistDetails?.notifyFlags?.users?.map( ( ele ) => ele?.value ) ];
|
|
2903
|
+
// let data = {
|
|
2904
|
+
// storeName: getchecklist[0].storeName,
|
|
2905
|
+
// flagCount: updateData.questionFlag,
|
|
2906
|
+
// checklistName: getchecklist[0].checkListName,
|
|
2907
|
+
// submittedBy: getchecklist[0].userName,
|
|
2908
|
+
// time: updateData.submitTime_string,
|
|
2909
|
+
// domain: `${JSON.parse( process.env.URL ).domain}/manage/trax/flags?date=${dayjs().format( 'YYYY-MM-DD' )}`,
|
|
2910
|
+
// };
|
|
2911
|
+
// const fileContent = fs.readFileSync( path.resolve( path.dirname( '' ) ) + '/src/hbs/flag.hbs', 'utf8' );
|
|
2912
|
+
// const htmlContent = handlebars.compile( fileContent );
|
|
2913
|
+
// const html = htmlContent( { data: data } );
|
|
2914
|
+
// emailList.forEach( ( email ) => {
|
|
2915
|
+
// let params = {
|
|
2916
|
+
// toEmail: email,
|
|
2917
|
+
// mailSubject: 'TangoEye | Checklist Flag',
|
|
2918
|
+
// htmlBody: html,
|
|
2919
|
+
// attachment: '',
|
|
2920
|
+
// sourceEmail: JSON.parse( process.env.SES ).adminEmail,
|
|
2921
|
+
// };
|
|
2922
|
+
// sendEmailWithSES( params.toEmail, params.mailSubject, params.htmlBody, params.attachment, params.sourceEmail );
|
|
2923
|
+
// } );
|
|
2924
|
+
// }
|
|
2925
|
+
// }
|
|
2907
2926
|
}
|
|
2908
2927
|
if ( getchecklist?.[0]?.redoEdit?.toString() != requestData?.redoEdit ) {
|
|
2909
2928
|
return res.sendSuccess( 'New Questions Added So,Checklist moved back to In Progress. Please complete the updated checklist.' );
|
|
@@ -620,6 +620,7 @@ export const zoneList = async ( req, res ) => {
|
|
|
620
620
|
const allowedChecklistsStreams = [
|
|
621
621
|
'Camera Angle Change Compliance',
|
|
622
622
|
];
|
|
623
|
+
console.log( allowedChecklists.includes( inputBody.checkListName ) );
|
|
623
624
|
|
|
624
625
|
if ( inputBody.checkListName && allowedChecklists.includes( inputBody.checkListName ) ) {
|
|
625
626
|
console.log( '==================' );
|
|
@@ -1914,7 +1915,6 @@ export const updateConfigure = async ( req, res ) => {
|
|
|
1914
1915
|
|
|
1915
1916
|
export const updateConfigurev1 = async ( req, res ) => {
|
|
1916
1917
|
try {
|
|
1917
|
-
console.log( '123' );
|
|
1918
1918
|
let inputBody = req.body;
|
|
1919
1919
|
let id;
|
|
1920
1920
|
let checklistDetails;
|
|
@@ -3972,6 +3972,7 @@ async function insertPCBulkV4( getCLconfig, checklistId, currentdate, updatedche
|
|
|
3972
3972
|
element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
|
|
3973
3973
|
element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
|
|
3974
3974
|
element4.complianceCount = getCLconfig?.complianceCount || 0;
|
|
3975
|
+
element4.userVerification = getCLconfig?.userVerification;
|
|
3975
3976
|
assignUserList.push( { ...element4 } );
|
|
3976
3977
|
}
|
|
3977
3978
|
} ) );
|
|
@@ -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
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
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
|
+
// }
|
package/src/hbs/flag.hbs
CHANGED
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
<td class="flagText" style="padding-left:30px; line-height: 24px;">No of Flags :</td>
|
|
177
177
|
<td></td>
|
|
178
178
|
<td></td>
|
|
179
|
-
<td class="flagText">{{data.flagCount}}</td>
|
|
179
|
+
<td class="flagText">{{data.flagCount}}{{#eq data.status 'submit'}}(RunAIFlag:{{data.runAIFlag}},QuestionFlag:{{data.questionFlag}}){{/eq}}</td>
|
|
180
180
|
</tr>
|
|
181
181
|
<tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
|
|
182
182
|
<td class="flagText" style="padding-left:30px; line-height: 24px;">Submitted By :</td>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { insertOpenSearchData, logger } from 'tango-app-api-middleware';
|
|
2
|
+
import { loggingConfig } from './config.js';
|
|
3
|
+
import { compressBatches } from './compressBatches.js';
|
|
4
|
+
|
|
5
|
+
export async function flushActivityLog( ctx, status ) {
|
|
6
|
+
if ( !loggingConfig.enabled ) return;
|
|
7
|
+
if ( !ctx ) return;
|
|
8
|
+
|
|
9
|
+
if ( ctx.steps.length === 0 && !ctx.error ) return;
|
|
10
|
+
|
|
11
|
+
const compressedSteps = safeCompress( ctx.steps );
|
|
12
|
+
|
|
13
|
+
const doc = {
|
|
14
|
+
version: loggingConfig.version,
|
|
15
|
+
requestId: ctx.requestId,
|
|
16
|
+
timestamp: new Date( ctx.startTime ).toISOString(),
|
|
17
|
+
duration: Date.now() - ctx.startTime,
|
|
18
|
+
user: ctx.user,
|
|
19
|
+
api: {
|
|
20
|
+
method: ctx.api.method,
|
|
21
|
+
path: ctx.api.path,
|
|
22
|
+
action: ctx.api.action,
|
|
23
|
+
body: stringifyBody( ctx.api.body ),
|
|
24
|
+
},
|
|
25
|
+
response: {
|
|
26
|
+
code: ctx.response?.code ?? null,
|
|
27
|
+
body: stringifyBody( ctx.response?.body ),
|
|
28
|
+
},
|
|
29
|
+
status,
|
|
30
|
+
steps: compressedSteps,
|
|
31
|
+
error: ctx.error,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await insertOpenSearchData( loggingConfig.index, doc );
|
|
36
|
+
} catch ( e ) {
|
|
37
|
+
logger.error( { functionName: 'flushActivityLog', error: e } );
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function safeCompress( steps ) {
|
|
42
|
+
try {
|
|
43
|
+
return compressBatches( steps );
|
|
44
|
+
} catch ( _e ) {
|
|
45
|
+
// Never let compression failure block log delivery — fall back to raw steps.
|
|
46
|
+
return steps;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function stringifyBody( body ) {
|
|
51
|
+
if ( body === null || body === undefined ) return null;
|
|
52
|
+
if ( typeof body === 'string' ) return body;
|
|
53
|
+
if ( body instanceof Error ) return body.message || String( body );
|
|
54
|
+
try {
|
|
55
|
+
return JSON.stringify( body );
|
|
56
|
+
} catch ( _e ) {
|
|
57
|
+
return '[unserializable]';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { activityLogStore, createLogContext } from './activityLogStore.js';
|
|
2
|
+
import { flushActivityLog } from './activityLogFlusher.js';
|
|
3
|
+
import { loggingConfig } from './config.js';
|
|
4
|
+
|
|
5
|
+
function refreshUser( ctx, req ) {
|
|
6
|
+
if ( req?.user ) {
|
|
7
|
+
ctx.user = {
|
|
8
|
+
_id: req.user._id,
|
|
9
|
+
userName: req.user.userName,
|
|
10
|
+
email: req.user.email,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function activityLogMiddleware( req, res, next ) {
|
|
16
|
+
if ( !loggingConfig.enabled ) return next();
|
|
17
|
+
|
|
18
|
+
const WRITE_METHODS = [ 'POST', 'PUT', 'PATCH', 'DELETE' ];
|
|
19
|
+
if ( !WRITE_METHODS.includes( req.method ) ) return next();
|
|
20
|
+
|
|
21
|
+
const ctx = createLogContext( req );
|
|
22
|
+
|
|
23
|
+
activityLogStore.run( ctx, () => {
|
|
24
|
+
const originalSendSuccess = res.sendSuccess;
|
|
25
|
+
const originalSendError = res.sendError;
|
|
26
|
+
|
|
27
|
+
res.sendSuccess = ( data ) => {
|
|
28
|
+
refreshUser( ctx, req );
|
|
29
|
+
ctx.response.code = 200;
|
|
30
|
+
ctx.response.body = data;
|
|
31
|
+
originalSendSuccess( data );
|
|
32
|
+
setImmediate( () => flushActivityLog( ctx, 'success' ).catch( () => {} ) );
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
res.sendError = ( message, code ) => {
|
|
36
|
+
refreshUser( ctx, req );
|
|
37
|
+
ctx.response.code = code ?? 500;
|
|
38
|
+
ctx.response.body = message;
|
|
39
|
+
originalSendError( message, code );
|
|
40
|
+
setImmediate( () => flushActivityLog( ctx, 'failed' ).catch( () => {} ) );
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
next();
|
|
44
|
+
} );
|
|
45
|
+
}
|