tango-app-api-analysis-traffic 3.8.7-vms.5 → 3.8.7-vms.6
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.6",
|
|
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 ) || {};
|
|
@@ -429,7 +428,6 @@ export async function footFallImages( req, res ) {
|
|
|
429
428
|
const actionTypesUser = [ 'tagging', 'finalreview' ];
|
|
430
429
|
temp = actionTypesUser.map( ( type ) => {
|
|
431
430
|
const mapping = getMappingForType( type );
|
|
432
|
-
|
|
433
431
|
const revisedFootfall = mapping.revisedFootfall ?? 0;
|
|
434
432
|
// Do not divide by 0
|
|
435
433
|
const revisedPerc =
|
|
@@ -438,7 +436,7 @@ export async function footFallImages( req, res ) {
|
|
|
438
436
|
'0';
|
|
439
437
|
|
|
440
438
|
// Since keys in count can be dynamic, just deep copy the count object or empty object
|
|
441
|
-
const countObj = mapping.count ?
|
|
439
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
442
440
|
|
|
443
441
|
return {
|
|
444
442
|
actionType: type,
|
|
@@ -446,6 +444,10 @@ export async function footFallImages( req, res ) {
|
|
|
446
444
|
revicedFootfall: revisedFootfall,
|
|
447
445
|
revicedPerc: revisedPerc,
|
|
448
446
|
count: countObj,
|
|
447
|
+
createdAt: mapping.createdAt ?? '',
|
|
448
|
+
createdByEmail: mapping.createdByEmail ??'',
|
|
449
|
+
createdByUserName: mapping.createdByUserName ??'',
|
|
450
|
+
createdByRole: mapping.createdByRole ??'',
|
|
449
451
|
};
|
|
450
452
|
} );
|
|
451
453
|
break;
|
|
@@ -462,7 +464,7 @@ export async function footFallImages( req, res ) {
|
|
|
462
464
|
'0';
|
|
463
465
|
|
|
464
466
|
// Since keys in count can be dynamic, just deep copy the count object or empty object
|
|
465
|
-
const countObj = mapping.count ?
|
|
467
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
466
468
|
|
|
467
469
|
return {
|
|
468
470
|
actionType: type,
|
|
@@ -470,6 +472,10 @@ export async function footFallImages( req, res ) {
|
|
|
470
472
|
revicedFootfall: revisedFootfall,
|
|
471
473
|
revicedPerc: revisedPerc,
|
|
472
474
|
count: countObj,
|
|
475
|
+
createdAt: mapping.createdAt ?? '',
|
|
476
|
+
createdByEmail: mapping.createdByEmail ??'',
|
|
477
|
+
createdByUserName: mapping.createdByUserName ??'',
|
|
478
|
+
createdByRole: mapping.createdByRole ??'',
|
|
473
479
|
};
|
|
474
480
|
} );
|
|
475
481
|
break;
|
|
@@ -478,8 +484,7 @@ export async function footFallImages( req, res ) {
|
|
|
478
484
|
const actionTypes = [ 'tagging', 'review', 'approve', 'finalreview' ];
|
|
479
485
|
temp = actionTypes.map( ( type ) => {
|
|
480
486
|
const mapping = getMappingForType( type );
|
|
481
|
-
|
|
482
|
-
const revisedFootfall = mapping.revisedFootfall ?? 0;
|
|
487
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
483
488
|
// Do not divide by 0
|
|
484
489
|
const revisedPerc =
|
|
485
490
|
footfallValue > 0 ?
|
|
@@ -487,7 +492,7 @@ export async function footFallImages( req, res ) {
|
|
|
487
492
|
'0';
|
|
488
493
|
|
|
489
494
|
// Since keys in count can be dynamic, just deep copy the count object or empty object
|
|
490
|
-
const countObj = mapping.count ?
|
|
495
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
491
496
|
|
|
492
497
|
return {
|
|
493
498
|
actionType: type,
|
|
@@ -495,6 +500,10 @@ export async function footFallImages( req, res ) {
|
|
|
495
500
|
revicedFootfall: revisedFootfall,
|
|
496
501
|
revicedPerc: revisedPerc,
|
|
497
502
|
count: countObj,
|
|
503
|
+
createdAt: mapping.createdAt ?? '',
|
|
504
|
+
createdByEmail: mapping.createdByEmail ??'',
|
|
505
|
+
createdByUserName: mapping.createdByUserName ??'',
|
|
506
|
+
createdByRole: mapping.createdByRole ??'',
|
|
498
507
|
};
|
|
499
508
|
} );
|
|
500
509
|
}
|
|
@@ -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();
|