tango-app-api-analysis-traffic 3.8.7-vms.5 → 3.8.7-vms.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-analysis-traffic",
|
|
3
|
-
"version": "3.8.7-vms.
|
|
3
|
+
"version": "3.8.7-vms.7",
|
|
4
4
|
"description": "Traffic Analysis",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"mongodb": "^6.8.0",
|
|
24
24
|
"nodemon": "^3.1.4",
|
|
25
25
|
"swagger-ui-express": "^5.0.1",
|
|
26
|
-
"tango-api-schema": "^2.4.
|
|
26
|
+
"tango-api-schema": "^2.4.29",
|
|
27
27
|
"tango-app-api-middleware": "^3.6.5",
|
|
28
28
|
"winston": "^3.13.1",
|
|
29
29
|
"winston-daily-rotate-file": "^5.0.0"
|
|
@@ -415,7 +415,6 @@ export async function footFallImages( req, res ) {
|
|
|
415
415
|
let temp = [];
|
|
416
416
|
const footfallValue = ticketDetails?._source?.footfallCount ?? 0;
|
|
417
417
|
const mappingInfoArray = ticketDetails?._source?.mappingInfo ?? [];
|
|
418
|
-
|
|
419
418
|
// Helper to get mappingInfo for an actionType
|
|
420
419
|
function getMappingForType( type ) {
|
|
421
420
|
return mappingInfoArray.find( ( m ) => m.type === type ) || {};
|
|
@@ -427,75 +426,138 @@ export async function footFallImages( req, res ) {
|
|
|
427
426
|
switch ( req.user.role ) {
|
|
428
427
|
case 'user':
|
|
429
428
|
const actionTypesUser = [ 'tagging', 'finalreview' ];
|
|
430
|
-
temp = actionTypesUser.map( ( type ) => {
|
|
431
|
-
const mapping = getMappingForType( type );
|
|
432
429
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
430
|
+
temp = [];
|
|
431
|
+
actionTypesUser.forEach( ( type ) => {
|
|
432
|
+
const mapping = getMappingForType( type );
|
|
433
|
+
if ( type === 'tagging' ) {
|
|
434
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
435
|
+
const revisedPerc =
|
|
436
|
+
footfallValue > 0 ?
|
|
437
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
438
|
+
'0';
|
|
439
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
440
|
+
temp.push( {
|
|
441
|
+
actionType: type,
|
|
442
|
+
footfall: footfallValue,
|
|
443
|
+
revicedFootfall: revisedFootfall,
|
|
444
|
+
revicedPerc: revisedPerc,
|
|
445
|
+
count: countObj,
|
|
446
|
+
createdAt: mapping.createdAt ?? '',
|
|
447
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
448
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
449
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
450
|
+
} );
|
|
451
|
+
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
452
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
453
|
+
const revisedPerc =
|
|
454
|
+
footfallValue > 0 ?
|
|
455
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
456
|
+
'0';
|
|
457
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
458
|
+
temp.push( {
|
|
459
|
+
actionType: type,
|
|
460
|
+
footfall: footfallValue,
|
|
461
|
+
revicedFootfall: revisedFootfall,
|
|
462
|
+
revicedPerc: revisedPerc,
|
|
463
|
+
count: countObj,
|
|
464
|
+
createdAt: mapping.createdAt ?? '',
|
|
465
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
466
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
467
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
468
|
+
} );
|
|
469
|
+
}
|
|
450
470
|
} );
|
|
451
471
|
break;
|
|
452
472
|
case 'admin':
|
|
453
473
|
const actionTypesAdmin = [ 'tagging', 'review', 'finalreview' ];
|
|
454
|
-
temp =
|
|
474
|
+
temp = [];
|
|
475
|
+
actionTypesAdmin.forEach( ( type ) => {
|
|
455
476
|
const mapping = getMappingForType( type );
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
477
|
+
if ( type === 'tagging' ) {
|
|
478
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
479
|
+
const revisedPerc =
|
|
480
|
+
footfallValue > 0 ?
|
|
481
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
482
|
+
'0';
|
|
483
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
484
|
+
temp.push( {
|
|
485
|
+
actionType: type,
|
|
486
|
+
footfall: footfallValue,
|
|
487
|
+
revicedFootfall: revisedFootfall,
|
|
488
|
+
revicedPerc: revisedPerc,
|
|
489
|
+
count: countObj,
|
|
490
|
+
createdAt: mapping.createdAt ?? '',
|
|
491
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
492
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
493
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
494
|
+
} );
|
|
495
|
+
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
496
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
497
|
+
const revisedPerc =
|
|
498
|
+
footfallValue > 0 ?
|
|
499
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
500
|
+
'0';
|
|
501
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
502
|
+
temp.push( {
|
|
503
|
+
actionType: type,
|
|
504
|
+
footfall: footfallValue,
|
|
505
|
+
revicedFootfall: revisedFootfall,
|
|
506
|
+
revicedPerc: revisedPerc,
|
|
507
|
+
count: countObj,
|
|
508
|
+
createdAt: mapping.createdAt ?? '',
|
|
509
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
510
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
511
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
512
|
+
} );
|
|
513
|
+
}
|
|
474
514
|
} );
|
|
475
|
-
break;
|
|
476
515
|
}
|
|
477
516
|
} else {
|
|
478
517
|
const actionTypes = [ 'tagging', 'review', 'approve', 'finalreview' ];
|
|
479
|
-
temp = actionTypes.map( ( type ) => {
|
|
480
|
-
const mapping = getMappingForType( type );
|
|
481
518
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
519
|
+
// Dynamically add to temp only if actionType matches and status is 'closed',
|
|
520
|
+
// except for 'tagging' where status must be 'raised'
|
|
521
|
+
temp = [];
|
|
522
|
+
actionTypes.forEach( ( type ) => {
|
|
523
|
+
const mapping = getMappingForType( type );
|
|
524
|
+
if ( type === 'tagging' ) {
|
|
525
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
526
|
+
const revisedPerc =
|
|
527
|
+
footfallValue > 0 ?
|
|
528
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
529
|
+
'0';
|
|
530
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
531
|
+
temp.push( {
|
|
532
|
+
actionType: type,
|
|
533
|
+
footfall: footfallValue,
|
|
534
|
+
revicedFootfall: revisedFootfall,
|
|
535
|
+
revicedPerc: revisedPerc,
|
|
536
|
+
count: countObj,
|
|
537
|
+
createdAt: mapping.createdAt ?? '',
|
|
538
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
539
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
540
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
541
|
+
} );
|
|
542
|
+
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
543
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
544
|
+
const revisedPerc =
|
|
545
|
+
footfallValue > 0 ?
|
|
546
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
547
|
+
'0';
|
|
548
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
549
|
+
temp.push( {
|
|
550
|
+
actionType: type,
|
|
551
|
+
footfall: footfallValue,
|
|
552
|
+
revicedFootfall: revisedFootfall,
|
|
553
|
+
revicedPerc: revisedPerc,
|
|
554
|
+
count: countObj,
|
|
555
|
+
createdAt: mapping.createdAt ?? '',
|
|
556
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
557
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
558
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
559
|
+
} );
|
|
560
|
+
}
|
|
499
561
|
} );
|
|
500
562
|
}
|
|
501
563
|
|
|
@@ -505,7 +567,7 @@ export async function footFallImages( req, res ) {
|
|
|
505
567
|
if ( resultData ) {
|
|
506
568
|
// temp.length? temp[0].status = 'open': null;
|
|
507
569
|
if ( resultData.status_code == '200' ) {
|
|
508
|
-
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
570
|
+
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
509
571
|
} else {
|
|
510
572
|
return res.sendError( 'No Content', 204 );
|
|
511
573
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getOpenSearchCount, logger } from 'tango-app-api-middleware';
|
|
2
|
-
import {
|
|
3
|
-
import { deleteByQuery } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
2
|
+
import { deleteByQuery, getOpenSearchData } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
4
3
|
import { findOne } from '../services/clients.services.js';
|
|
5
4
|
|
|
6
5
|
export async function getTaggingConfig( req, res, next ) {
|
|
@@ -65,41 +64,54 @@ export async function getFootfallCount( req, res, next ) {
|
|
|
65
64
|
export async function mappingConfig( req, res, next ) {
|
|
66
65
|
try {
|
|
67
66
|
const inputData = req.body;
|
|
67
|
+
|
|
68
68
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
term: {
|
|
77
|
-
'storeId.keyword': inputData.storeId,
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
term: {
|
|
82
|
-
'dateString': inputData.dateString,
|
|
83
|
-
},
|
|
69
|
+
const footfallQuery ={
|
|
70
|
+
query: {
|
|
71
|
+
bool: {
|
|
72
|
+
must: [
|
|
73
|
+
{
|
|
74
|
+
term: {
|
|
75
|
+
'store_id.keyword': inputData.storeId,
|
|
84
76
|
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
term: {
|
|
80
|
+
'date_string': inputData.dateString,
|
|
89
81
|
},
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
],
|
|
92
85
|
},
|
|
93
|
-
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
|
|
90
|
+
const footfallOutput = await getOpenSearchData( openSearch.footfall, footfallQuery );
|
|
91
|
+
if ( footfallOutput?.body?.hits?.hits?.length === 0 ) {
|
|
92
|
+
return res.sendError( 'No updated footfall for this date', 400 );
|
|
93
|
+
}
|
|
94
|
+
const getFootfallCount = footfallOutput?.body?.hits?.hits;
|
|
95
|
+
const footfall = getFootfallCount?.[0]?._source?.footfall_count;
|
|
96
|
+
|
|
97
|
+
const getConfig = await findOne( { clientId: inputData?.storeId?.split( '-' )[0] }, { footfallDirectoryConfigs: 1 } );
|
|
98
|
+
const taggingLimitation = getConfig?.footfallDirectoryConfigs?.taggingLimitation;
|
|
99
|
+
// Find the tagging limitation for the given revopsType
|
|
100
|
+
let matchedLimitation = null;
|
|
101
|
+
if ( Array.isArray( taggingLimitation ) ) {
|
|
102
|
+
matchedLimitation = taggingLimitation.find(
|
|
103
|
+
( l ) => l.type === inputData.revopsType,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if ( matchedLimitation ) {
|
|
108
|
+
// Determine the limit value
|
|
109
|
+
let limitValue = Number( matchedLimitation.value ) || 0;
|
|
110
|
+
let unit = matchedLimitation.unit;
|
|
111
|
+
|
|
112
|
+
// Assuming getData and/or getOpenSearchCount provides the actual tagged count for revopsType
|
|
113
|
+
// Query OpenSearch for current tagged count for this revopsType
|
|
114
|
+
const taggedCountQuery = {
|
|
103
115
|
query: {
|
|
104
116
|
bool: {
|
|
105
117
|
must: [
|
|
@@ -118,92 +130,51 @@ export async function mappingConfig( req, res, next ) {
|
|
|
118
130
|
'revopsType.keyword': inputData.revopsType,
|
|
119
131
|
},
|
|
120
132
|
},
|
|
133
|
+
{
|
|
134
|
+
term: {
|
|
135
|
+
'isParent': false,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
121
138
|
],
|
|
122
139
|
},
|
|
123
140
|
},
|
|
124
141
|
};
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
142
|
+
const taggedData = await getOpenSearchCount( openSearch.revop, taggedCountQuery );
|
|
143
|
+
const taggedValue = ( taggedData?.body?.count || 0 )+ ( inputData.revopsType == 'duplicate'? inputData?.duplicateImage?.length : 1 );
|
|
144
|
+
// If the unit is %, compare percentage of taggedValue/footfall, otherwise compare taggedValue to limitValue directly
|
|
145
|
+
let isLimitExceeded = false;
|
|
146
|
+
if ( unit === '%' ) {
|
|
147
|
+
// footfall may be undefined, treat as 0 (avoid division by zero)
|
|
148
|
+
const totalFootfall = Number( footfall ) || 0;
|
|
149
|
+
const taggedPercent = totalFootfall > 0 ? ( taggedValue / totalFootfall ) * 100 : 0;
|
|
150
|
+
isLimitExceeded = taggedPercent > limitValue;
|
|
151
|
+
logger.info( {
|
|
152
|
+
limitType: 'PERCENT',
|
|
153
|
+
taggedValue,
|
|
154
|
+
totalFootfall,
|
|
155
|
+
taggedPercent,
|
|
156
|
+
limitValue,
|
|
157
|
+
isLimitExceeded,
|
|
158
|
+
forRevopsType: inputData.revopsType,
|
|
159
|
+
} );
|
|
128
160
|
} else {
|
|
129
|
-
|
|
161
|
+
// Non-percent, treat limitValue as an absolute number
|
|
162
|
+
isLimitExceeded = taggedValue > limitValue;
|
|
163
|
+
logger.info( {
|
|
164
|
+
limitType: 'ABSOLUTE',
|
|
165
|
+
taggedValue,
|
|
166
|
+
limitValue,
|
|
167
|
+
isLimitExceeded,
|
|
168
|
+
forRevopsType: inputData.revopsType,
|
|
169
|
+
} );
|
|
130
170
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
next();
|
|
134
|
-
return;
|
|
135
|
-
} else {
|
|
136
|
-
return res.sendError( 'Forbidden to junk mapping', 500 );
|
|
171
|
+
if ( isLimitExceeded ) {
|
|
172
|
+
return res.sendError( `Limit exceed: Only ${limitValue}${unit || ''} items allowed for ${inputData.revopsType}`, 400 );
|
|
137
173
|
}
|
|
174
|
+
return next();
|
|
138
175
|
} else {
|
|
139
|
-
next();
|
|
176
|
+
return next();
|
|
140
177
|
}
|
|
141
|
-
// else if ( inputData.revopsType == 'duplicate' ) {
|
|
142
|
-
// const getFootfallQuery = {
|
|
143
|
-
// query: {
|
|
144
|
-
// terms: {
|
|
145
|
-
// _id: [ inputData?.dateString ],
|
|
146
|
-
// },
|
|
147
|
-
// },
|
|
148
|
-
// _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
149
|
-
// sort: [
|
|
150
|
-
// {
|
|
151
|
-
// date_iso: {
|
|
152
|
-
// order: 'desc',
|
|
153
|
-
// },
|
|
154
|
-
// },
|
|
155
|
-
// ],
|
|
156
|
-
// };
|
|
157
|
-
|
|
158
|
-
// const getFootfall = await getOpenSearchData( openSearch.footfall, getFootfallQuery );
|
|
159
|
-
// const footfall = getFootfall?.body?.hites?.hits?.[0]?._source?.footfall_count;
|
|
160
|
-
// const getQuery = {
|
|
161
|
-
// query: {
|
|
162
|
-
// bool: {
|
|
163
|
-
// must: [
|
|
164
|
-
// {
|
|
165
|
-
// term: {
|
|
166
|
-
// 'storeId.keyword': inputData.storeId,
|
|
167
|
-
// },
|
|
168
|
-
// },
|
|
169
|
-
// {
|
|
170
|
-
// term: {
|
|
171
|
-
// 'dateString': inputData.dateString,
|
|
172
|
-
// },
|
|
173
|
-
// },
|
|
174
|
-
// {
|
|
175
|
-
// term: {
|
|
176
|
-
// 'revopsType.keyword': inputData.revopsType,
|
|
177
|
-
// },
|
|
178
|
-
// },
|
|
179
|
-
// {
|
|
180
|
-
// term: {
|
|
181
|
-
// 'parent.keyword': null,
|
|
182
|
-
// },
|
|
183
|
-
// },
|
|
184
|
-
// ],
|
|
185
|
-
// },
|
|
186
|
-
// },
|
|
187
|
-
// };
|
|
188
|
-
// const getData = await getOpenSearchCount( openSearch.revop, getQuery );
|
|
189
|
-
// logger.info( { getData: getData, footfall: footfall, duplicate: config?.revopTagging?.duplicate } );
|
|
190
|
-
// if ( getData && footfall && config?.revopTagging?.duplicate ) {
|
|
191
|
-
// const data = config?.revopTagging?.duplicate;
|
|
192
|
-
// // Convert "20%" → 0.2 (handle both "20%" and 20)
|
|
193
|
-
// const percentStr = typeof data === 'string' ? data.replace( '%', '' ) : data;
|
|
194
|
-
// logger.info( { percentStr: percentStr } );
|
|
195
|
-
// const percentValue =percentStr / 100;
|
|
196
|
-
|
|
197
|
-
// const result = percentValue * footfall;
|
|
198
|
-
|
|
199
|
-
// logger.info( { result: result, footfall: footfall } );
|
|
200
|
-
// }
|
|
201
|
-
// if ( getData && getData?.body?.count >= Math.round( result ) ) {
|
|
202
|
-
// return res.sendError( `Select up to ${config?.revopTagging?.duplicate} items only`, 400 );
|
|
203
|
-
// } else {
|
|
204
|
-
// next();
|
|
205
|
-
// }
|
|
206
|
-
// }
|
|
207
178
|
} catch ( error ) {
|
|
208
179
|
logger.error( { error: error, message: req.body, function: 'traffic-revop-getTaggingConfig' } );
|
|
209
180
|
next();
|