tango-app-api-analysis-traffic 3.8.7-vms.9 → 3.8.8
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.
|
|
3
|
+
"version": "3.8.8",
|
|
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.9",
|
|
27
27
|
"tango-app-api-middleware": "^3.6.5",
|
|
28
28
|
"winston": "^3.13.1",
|
|
29
29
|
"winston-daily-rotate-file": "^5.0.0"
|
|
@@ -2,8 +2,7 @@ import { logger, insertOpenSearchData, getOpenSearchData, updateOpenSearchData }
|
|
|
2
2
|
import { findOnerevopConfig } from '../services/revopConfig.service.js';
|
|
3
3
|
import * as clientService from '../services/clients.services.js';
|
|
4
4
|
import { bulkUpdate, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
5
|
-
import
|
|
6
|
-
// import dayjs from 'dayjs';
|
|
5
|
+
import dayjs from 'dayjs';
|
|
7
6
|
// Lamda Service Call //
|
|
8
7
|
async function LamdaServiceCall( url, data ) {
|
|
9
8
|
try {
|
|
@@ -212,171 +211,44 @@ export async function storeProcessedData( req, res ) {
|
|
|
212
211
|
try {
|
|
213
212
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
214
213
|
const inputData = req.query;
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
let start = dayjs( fromDate );
|
|
224
|
-
// get start value from the before day one
|
|
225
|
-
// Move start one day back so we can access "day before" when looping
|
|
226
|
-
start = start.subtract( 1, 'day' );
|
|
227
|
-
let end = dayjs( toDate );
|
|
228
|
-
|
|
229
|
-
if ( !start.isValid() || !end.isValid() ) {
|
|
230
|
-
return res.sendError( 'Invalid date range supplied', 400 );
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if ( end.isBefore( start ) ) {
|
|
234
|
-
[ start, end ] = [ end, start ];
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const allDateStrings = [];
|
|
238
|
-
const orderedDates = [];
|
|
239
|
-
|
|
240
|
-
while ( start.isSameOrBefore( end ) ) {
|
|
241
|
-
const formatted = start.format( 'YYYY-MM-DD' );
|
|
242
|
-
orderedDates.push( formatted );
|
|
243
|
-
allDateStrings.push( `${storeId}_${formatted}` );
|
|
244
|
-
start = start.add( 1, 'day' );
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if ( allDateStrings.length === 0 ) {
|
|
248
|
-
return res.sendSuccess( [] );
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const footfallQuery = {
|
|
252
|
-
query: {
|
|
253
|
-
terms: {
|
|
254
|
-
_id: allDateStrings,
|
|
255
|
-
},
|
|
214
|
+
const previousDate = dayjs( inputData.dateString ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
|
|
215
|
+
const dateString = `${inputData.storeId}_${inputData.dateString}`;
|
|
216
|
+
const dateStringPrevious = `${inputData.storeId}_${previousDate}`;
|
|
217
|
+
const getQuery = {
|
|
218
|
+
query: {
|
|
219
|
+
terms: {
|
|
220
|
+
_id: [ dateString, dateStringPrevious ],
|
|
256
221
|
},
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const multiGet = await getOpenSearchData( openSearch.footfall, footfallQuery );
|
|
265
|
-
const multiHits = multiGet?.body?.hits?.hits || [];
|
|
266
|
-
const hitsMap = new Map();
|
|
267
|
-
multiHits.forEach( ( hit ) => {
|
|
268
|
-
hitsMap.set( hit?._id, hit?._source || null );
|
|
269
|
-
} );
|
|
270
|
-
|
|
271
|
-
const responseArray = [];
|
|
272
|
-
|
|
273
|
-
for ( let i = 1; i < orderedDates.length; i++ ) {
|
|
274
|
-
const currentDate = orderedDates[i];
|
|
275
|
-
const currentId = `${storeId}_${currentDate}`;
|
|
276
|
-
logger.info( { currentId, currentDate } );
|
|
277
|
-
const processedData = hitsMap.get( currentId );
|
|
278
|
-
logger.info( { processedData } );
|
|
279
|
-
if ( !processedData ) {
|
|
280
|
-
responseArray.push( {
|
|
281
|
-
date: currentDate,
|
|
282
|
-
footfallCount: 0,
|
|
283
|
-
footfallCountTrend: 0,
|
|
284
|
-
downtime: 0,
|
|
285
|
-
} );
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const prevDate = dayjs( currentDate ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
|
|
290
|
-
const prevId = `${storeId}_${prevDate}`;
|
|
291
|
-
logger.info( { prevId, prevDate } );
|
|
292
|
-
const previousData = hitsMap.get( prevId );
|
|
293
|
-
|
|
294
|
-
let footfallCountTrend = 0;
|
|
295
|
-
logger.info( { previousData, previoucubr1: previousData?.footfall_count } );
|
|
296
|
-
if ( previousData && previousData.footfall_count ) {
|
|
297
|
-
logger.info( { previousData, previoucubr: previousData?.footfall_count } );
|
|
298
|
-
footfallCountTrend = Math.round(
|
|
299
|
-
( ( processedData.footfall_count - previousData?.footfall_count ) / previousData.footfall_count ) * 100,
|
|
300
|
-
);
|
|
301
|
-
logger.info( { footfallCountTrend } );
|
|
302
|
-
}
|
|
303
|
-
// Add ticket status from openSearch.footfallDirectory (_source.status)
|
|
304
|
-
let ticketStatus = null;
|
|
305
|
-
// Try to find a matching footfallDirectory record for this date+storeId
|
|
306
|
-
// const ticketKey = `${storeId}_${currentDate}`;
|
|
307
|
-
const footfallDirQuery = {
|
|
308
|
-
query: {
|
|
309
|
-
bool: {
|
|
310
|
-
must: [
|
|
311
|
-
{ term: { 'storeId.keyword': storeId } },
|
|
312
|
-
{ term: { 'dateString': currentDate } },
|
|
313
|
-
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
314
|
-
],
|
|
315
|
-
},
|
|
222
|
+
},
|
|
223
|
+
_source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
224
|
+
sort: [
|
|
225
|
+
{
|
|
226
|
+
date_iso: {
|
|
227
|
+
order: 'desc',
|
|
316
228
|
},
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
try {
|
|
321
|
-
const footfallDirRes = await getOpenSearchData( openSearch.footfallDirectory, footfallDirQuery );
|
|
322
|
-
const hit = footfallDirRes?.body?.hits?.hits?.[0];
|
|
323
|
-
if ( hit?._source?.mappingInfo && Array.isArray( hit._source.mappingInfo ) ) {
|
|
324
|
-
for ( let i = 0; i < hit._source.mappingInfo.length; i++ ) {
|
|
325
|
-
if ( hit._source.mappingInfo[i].type === 'tagging' ) {
|
|
326
|
-
ticketStatus = hit._source.mappingInfo[i].status;
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
} catch ( err ) {
|
|
332
|
-
logger.warn( { message: 'Could not get ticket status from footfallDirectory', error: err } );
|
|
333
|
-
}
|
|
334
|
-
// Check if request status ("raised") should be enabled or disabled by querying MongoDB config
|
|
335
|
-
// We'll assume findOnerevopConfig can fetch the record with status for this storeId and dateString
|
|
336
|
-
let raisedStatusEnabled = 'reset'; // default: enabled
|
|
337
|
-
try {
|
|
338
|
-
const mongoConfig = await findOneVmsStoreRequest( { storeId: storeId, dateString: currentDate } );
|
|
339
|
-
if ( mongoConfig && mongoConfig.status ) {
|
|
340
|
-
raisedStatusEnabled = mongoConfig.status;
|
|
341
|
-
}
|
|
342
|
-
} catch ( err ) {
|
|
343
|
-
logger.warn( { message: 'Could not get request status from MongoDB', error: err } );
|
|
344
|
-
// Leave raisedStatusEnabled as default
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
responseArray.push( {
|
|
348
|
-
date: processedData?.date_string || currentDate,
|
|
349
|
-
footfallCount: processedData?.footfall_count || 0,
|
|
350
|
-
footfallCountTrend,
|
|
351
|
-
downtime: processedData?.down_time || 0,
|
|
352
|
-
ticketStatus,
|
|
353
|
-
raisedStatusEnabled,
|
|
354
|
-
|
|
355
|
-
} );
|
|
356
|
-
|
|
357
|
-
if ( raisedStatusEnabled === 'block' ) {
|
|
358
|
-
// Calculate the number of days from currentDate + 1 to the end of the month
|
|
359
|
-
// Assume currentDate is in format 'YYYY-MM-DD'
|
|
360
|
-
const currentDateObj = new Date( currentDate );
|
|
361
|
-
// Move to next day
|
|
362
|
-
const nextDay = new Date( currentDateObj );
|
|
363
|
-
nextDay.setDate( currentDateObj.getDate() + 1 );
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
};
|
|
364
232
|
|
|
365
|
-
|
|
366
|
-
|
|
233
|
+
const getData = await getOpenSearchData( openSearch.footfall, getQuery );
|
|
234
|
+
const hits = getData?.body?.hits?.hits || [];
|
|
367
235
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if ( noOfBlockedDays < 0 ) noOfBlockedDays = 0;
|
|
236
|
+
const processedData = hits.find( ( d ) => d._id === dateString )?._source || null;
|
|
237
|
+
const previousData = hits.find( ( d ) => d._id === dateStringPrevious )?._source || null;
|
|
371
238
|
|
|
372
|
-
|
|
373
|
-
responseArray[responseArray.length - 1].noOfBlockedDays = noOfBlockedDays;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
239
|
+
let footfallCountTrend = 0;
|
|
376
240
|
|
|
377
|
-
|
|
241
|
+
if ( processedData && previousData && previousData.footfall_count ) {
|
|
242
|
+
footfallCountTrend = Math.round(
|
|
243
|
+
( ( processedData.footfall_count - previousData.footfall_count ) / previousData.footfall_count ) * 100,
|
|
244
|
+
);
|
|
378
245
|
}
|
|
379
|
-
|
|
246
|
+
|
|
247
|
+
return res.sendSuccess( {
|
|
248
|
+
footfallCount: processedData?.footfall_count || 0,
|
|
249
|
+
footfallCountTrend,
|
|
250
|
+
downtime: processedData?.down_time || 0,
|
|
251
|
+
} );
|
|
380
252
|
} catch ( error ) {
|
|
381
253
|
logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
|
|
382
254
|
const err = error.message || 'Internal Server Error';
|
|
@@ -415,177 +287,40 @@ export async function footFallImages( req, res ) {
|
|
|
415
287
|
],
|
|
416
288
|
},
|
|
417
289
|
},
|
|
418
|
-
'_source': [ 'dateString', 'storeId', '
|
|
290
|
+
'_source': [ 'dateString', 'storeId', 'duplicateCount', 'footfallCount', 'employeeCount', 'houseKeepingCount', 'junkCount', 'status', 'ticketId', 'comments', 'userName', 'role', 'createdAt', 'email', 'houseKeepingACCount', 'duplicateACCount', 'employeeACCount', 'junkACCount', 'approverEmail', 'approverRole', 'approverUserName' ],
|
|
419
291
|
|
|
420
292
|
};
|
|
421
293
|
|
|
422
294
|
const getData = await getOpenSearchData( opensearch.footfallDirectory, query );
|
|
423
295
|
const ticketDetails = getData?.body?.hits?.hits[0];
|
|
424
296
|
let temp = [];
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
revicedFootfall: revisedFootfall,
|
|
453
|
-
revicedPerc: revisedPerc,
|
|
454
|
-
count: countObj,
|
|
455
|
-
createdAt: mapping.createdAt ?? '',
|
|
456
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
457
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
458
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
459
|
-
isUp: false,
|
|
460
|
-
} );
|
|
461
|
-
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
462
|
-
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
463
|
-
const revisedPerc =
|
|
464
|
-
footfallValue > 0 ?
|
|
465
|
-
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
466
|
-
'0';
|
|
467
|
-
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
468
|
-
temp.push( {
|
|
469
|
-
actionType: type,
|
|
470
|
-
footfall: footfallValue,
|
|
471
|
-
revicedFootfall: revisedFootfall,
|
|
472
|
-
revicedPerc: revisedPerc,
|
|
473
|
-
count: countObj,
|
|
474
|
-
createdAt: mapping.createdAt ?? '',
|
|
475
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
476
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
477
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
478
|
-
} );
|
|
479
|
-
}
|
|
480
|
-
} );
|
|
481
|
-
break;
|
|
482
|
-
case 'admin':
|
|
483
|
-
const actionTypesAdmin = [ 'tagging', 'review', 'finalreview' ];
|
|
484
|
-
temp = [];
|
|
485
|
-
actionTypesAdmin.forEach( ( type ) => {
|
|
486
|
-
const mapping = getMappingForType( type );
|
|
487
|
-
if ( type === 'tagging' ) {
|
|
488
|
-
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
489
|
-
const revisedPerc =
|
|
490
|
-
footfallValue > 0 ?
|
|
491
|
-
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
492
|
-
'0';
|
|
493
|
-
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
494
|
-
temp.push( {
|
|
495
|
-
actionType: type,
|
|
496
|
-
footfall: footfallValue,
|
|
497
|
-
revicedFootfall: revisedFootfall,
|
|
498
|
-
revicedPerc: revisedPerc,
|
|
499
|
-
count: countObj,
|
|
500
|
-
createdAt: mapping.createdAt ?? '',
|
|
501
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
502
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
503
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
504
|
-
isUp: false,
|
|
505
|
-
} );
|
|
506
|
-
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
507
|
-
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
508
|
-
const revisedPerc =
|
|
509
|
-
footfallValue > 0 ?
|
|
510
|
-
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
511
|
-
'0';
|
|
512
|
-
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
513
|
-
temp.push( {
|
|
514
|
-
actionType: type,
|
|
515
|
-
footfall: footfallValue,
|
|
516
|
-
revicedFootfall: revisedFootfall,
|
|
517
|
-
revicedPerc: revisedPerc,
|
|
518
|
-
count: countObj,
|
|
519
|
-
createdAt: mapping.createdAt ?? '',
|
|
520
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
521
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
522
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
523
|
-
} );
|
|
524
|
-
}
|
|
525
|
-
} );
|
|
526
|
-
}
|
|
527
|
-
} else {
|
|
528
|
-
const actionTypes = [ 'tagging', 'review', 'approve', 'finalreview' ];
|
|
529
|
-
|
|
530
|
-
// Dynamically add to temp only if actionType matches and status is 'closed',
|
|
531
|
-
// except for 'tagging' where status must be 'raised'
|
|
532
|
-
temp = [];
|
|
533
|
-
actionTypes.forEach( ( type ) => {
|
|
534
|
-
const mapping = getMappingForType( type );
|
|
535
|
-
if ( type === 'tagging' ) {
|
|
536
|
-
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
537
|
-
const revisedPerc =
|
|
538
|
-
footfallValue > 0 ?
|
|
539
|
-
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
540
|
-
'0';
|
|
541
|
-
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
542
|
-
temp.push( {
|
|
543
|
-
actionType: type,
|
|
544
|
-
footfall: footfallValue,
|
|
545
|
-
revicedFootfall: revisedFootfall,
|
|
546
|
-
revicedPerc: revisedPerc,
|
|
547
|
-
count: countObj,
|
|
548
|
-
createdAt: mapping.createdAt ?? '',
|
|
549
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
550
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
551
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
552
|
-
isUp: false,
|
|
553
|
-
} );
|
|
554
|
-
} else if ( type !== 'tagging' && mapping.status === 'closed' ) {
|
|
555
|
-
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
556
|
-
const revisedPerc =
|
|
557
|
-
footfallValue > 0 ?
|
|
558
|
-
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
559
|
-
'0';
|
|
560
|
-
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
561
|
-
temp.push( {
|
|
562
|
-
actionType: type,
|
|
563
|
-
footfall: footfallValue,
|
|
564
|
-
revicedFootfall: revisedFootfall,
|
|
565
|
-
revicedPerc: revisedPerc,
|
|
566
|
-
count: countObj,
|
|
567
|
-
createdAt: mapping.createdAt ?? '',
|
|
568
|
-
createdByEmail: mapping.createdByEmail ?? '',
|
|
569
|
-
createdByUserName: mapping.createdByUserName ?? '',
|
|
570
|
-
createdByRole: mapping.createdByRole ?? '',
|
|
571
|
-
} );
|
|
572
|
-
}
|
|
573
|
-
} );
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const LamdaURL = revop.getImages;
|
|
578
|
-
let resultData = await LamdaServiceCall( LamdaURL, inputData );
|
|
579
|
-
if ( resultData ) {
|
|
580
|
-
// temp.length? temp[0].status = 'open': null;
|
|
581
|
-
if ( resultData.status_code == '200' ) {
|
|
582
|
-
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
583
|
-
} else {
|
|
584
|
-
return res.sendError( 'No Content', 204 );
|
|
585
|
-
}
|
|
586
|
-
} else {
|
|
587
|
-
return res.sendError( 'No Content', 204 );
|
|
588
|
-
}
|
|
297
|
+
ticketDetails?._source? temp.push( ticketDetails?._source ) :null;
|
|
298
|
+
// temp[0].status = 'open';
|
|
299
|
+
if ( ticketDetails?._source?.status == 'closed' ) {
|
|
300
|
+
delete temp[0].status;
|
|
301
|
+
temp.push( { ...ticketDetails?._source, status: 'closed' } );
|
|
302
|
+
|
|
303
|
+
temp[1].userName = getData?.body?.hits?.hits?.[0]?._source?.approverUserName;
|
|
304
|
+
temp[1].email = getData?.body?.hits?.hits?.[0]?._source?.approverEmail;
|
|
305
|
+
temp[1].role = getData?.body?.hits?.hits?.[0]?._source?.approverRole;
|
|
306
|
+
temp[1].employeeCount = getData?.body?.hits?.hits?.[0]?._source?.employeeACCount;
|
|
307
|
+
temp[1].houseKeepingCount = getData?.body?.hits?.hits?.[0]?._source?.houseKeepingACCount;
|
|
308
|
+
temp[1].duplicateCount = getData?.body?.hits?.hits?.[0]?._source?.duplicateACCount;
|
|
309
|
+
temp[1].junkCount = getData?.body?.hits?.hits?.[0]?._source?.junkACCount;
|
|
310
|
+
}
|
|
311
|
+
const LamdaURL = revop.getImages;
|
|
312
|
+
let resultData = await LamdaServiceCall( LamdaURL, inputData );
|
|
313
|
+
logger.info( { resultData: resultData } );
|
|
314
|
+
if ( resultData ) {
|
|
315
|
+
temp.length? temp[0].status = 'open': null;
|
|
316
|
+
if ( resultData.status_code == '200' ) {
|
|
317
|
+
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0? temp : null, config: req?.store?.revopTagging } );
|
|
318
|
+
} else {
|
|
319
|
+
return res.sendError( 'No Content', 204 );
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
return res.sendError( 'No Content', 204 );
|
|
323
|
+
}
|
|
589
324
|
} catch ( error ) {
|
|
590
325
|
logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
|
|
591
326
|
const err = error.message || 'Internal Server Error';
|
|
@@ -601,7 +336,6 @@ export async function tagTempId( req, res ) {
|
|
|
601
336
|
clientId: inputData.storeId.split( '-' )[0],
|
|
602
337
|
storeId: inputData.storeId,
|
|
603
338
|
tempId: inputData.tempId,
|
|
604
|
-
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
605
339
|
dateString: inputData.dateString,
|
|
606
340
|
timeRange: inputData.timeRange,
|
|
607
341
|
processType: inputData.processType,
|
|
@@ -614,15 +348,6 @@ export async function tagTempId( req, res ) {
|
|
|
614
348
|
isChecked: inputData.isChecked,
|
|
615
349
|
duplicateImage: inputData?.duplicateImage?.length>0? inputData?.duplicateImage :[],
|
|
616
350
|
type: 'tagging-reflect',
|
|
617
|
-
ticketStatus: 'submitted',
|
|
618
|
-
isParent: inputData?.revopsType === 'duplicate'? true : false,
|
|
619
|
-
actions: [
|
|
620
|
-
{
|
|
621
|
-
actionType: 'tagging',
|
|
622
|
-
action: 'submitted',
|
|
623
|
-
},
|
|
624
|
-
],
|
|
625
|
-
comments: inputData.comments || '',
|
|
626
351
|
createdAt: new Date(),
|
|
627
352
|
updatedAt: new Date(),
|
|
628
353
|
|
|
@@ -661,7 +386,6 @@ export async function tagTempId( req, res ) {
|
|
|
661
386
|
clientId: inputData.storeId.split( '-' )[0],
|
|
662
387
|
storeId: inputData.storeId,
|
|
663
388
|
tempId: item.tempId,
|
|
664
|
-
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
665
389
|
dateString: inputData.dateString,
|
|
666
390
|
timeRange: item.timeRange,
|
|
667
391
|
isChecked: item.isChecked,
|
|
@@ -673,16 +397,7 @@ export async function tagTempId( req, res ) {
|
|
|
673
397
|
status: item?.revopsType == 'non-tagging' ?'':'submitted',
|
|
674
398
|
description: '',
|
|
675
399
|
duplicateImage: [],
|
|
676
|
-
isParent: false,
|
|
677
400
|
type: 'tagging-reflect',
|
|
678
|
-
ticketStatus: 'submitted',
|
|
679
|
-
comments: inputData.comments || '',
|
|
680
|
-
actions: [
|
|
681
|
-
{
|
|
682
|
-
actionType: 'tagging',
|
|
683
|
-
action: 'submitted',
|
|
684
|
-
},
|
|
685
|
-
],
|
|
686
401
|
createdAt: new Date(),
|
|
687
402
|
updatedAt: new Date(),
|
|
688
403
|
|
package/src/dtos/revop.dtos.js
CHANGED
|
@@ -4,8 +4,7 @@ import dayjs from 'dayjs';
|
|
|
4
4
|
export const storeProcessedDataSchema = joi.object( {
|
|
5
5
|
|
|
6
6
|
storeId: joi.string().required(),
|
|
7
|
-
|
|
8
|
-
toDate: joi.string().required(),
|
|
7
|
+
dateString: joi.string().required(),
|
|
9
8
|
|
|
10
9
|
} );
|
|
11
10
|
|
|
@@ -56,8 +55,8 @@ export const tagTempIdSchema = joi.object( {
|
|
|
56
55
|
|
|
57
56
|
const diff = today.diff( inputDate, 'day' );
|
|
58
57
|
|
|
59
|
-
if ( diff >
|
|
60
|
-
return helpers.message( 'Tagging is not allowed for a period exceeding
|
|
58
|
+
if ( diff > 3 ) {
|
|
59
|
+
return helpers.message( 'Tagging is not allowed for a period exceeding 3 days' );
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
return value;
|
|
@@ -66,9 +65,6 @@ export const tagTempIdSchema = joi.object( {
|
|
|
66
65
|
revopsType: joi.string().required(),
|
|
67
66
|
timeRange: joi.string().required(),
|
|
68
67
|
isChecked: joi.boolean().required().allow( null ),
|
|
69
|
-
mode: joi.string().valid( 'web', 'mobile' ).required().messages( {
|
|
70
|
-
'any.only': 'type must be one of [mobile,web]',
|
|
71
|
-
} ),
|
|
72
68
|
duplicateImage: joi.array().items( joi.object(
|
|
73
69
|
{
|
|
74
70
|
tempId: joi.number().required(),
|
|
@@ -84,7 +80,6 @@ export const tagTempIdSchema = joi.object( {
|
|
|
84
80
|
entryTime: joi.string().required(),
|
|
85
81
|
exitTime: joi.string().required(),
|
|
86
82
|
filePath: joi.string().required(),
|
|
87
|
-
comments: joi.string().optional().allow( '' ),
|
|
88
83
|
|
|
89
84
|
} );
|
|
90
85
|
|
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
import { getOpenSearchCount, logger } from 'tango-app-api-middleware';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { findOneStore } from '../services/stores.service.js';
|
|
3
|
+
import { deleteByQuery } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
4
4
|
|
|
5
5
|
export async function getTaggingConfig( req, res, next ) {
|
|
6
6
|
try {
|
|
7
7
|
const inputData= req.query;
|
|
8
|
-
const
|
|
9
|
-
const getData = await findOne( { clientId: clientId }, { footfallDirectoryConfigs: 1 } );
|
|
10
|
-
|
|
11
|
-
// Convert "taggingLimitation" array (if present) to "config" object with expected key-value pairs
|
|
12
|
-
let config = {};
|
|
13
|
-
if ( getData && Array.isArray( getData.taggingLimitation ) ) {
|
|
14
|
-
for ( const item of getData.taggingLimitation ) {
|
|
15
|
-
if ( item && item.type && typeof item.value !== 'undefined' && item.unit ) {
|
|
16
|
-
config[item.type] = `${item.value}${item.unit}`;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
getData.config = config;
|
|
21
|
-
|
|
8
|
+
const getData = await findOneStore( { storeId: inputData.storeId }, { revopTagging: 1 } );
|
|
22
9
|
req.store = getData;
|
|
23
10
|
next();
|
|
24
11
|
} catch ( error ) {
|
|
@@ -64,54 +51,10 @@ export async function getFootfallCount( req, res, next ) {
|
|
|
64
51
|
export async function mappingConfig( req, res, next ) {
|
|
65
52
|
try {
|
|
66
53
|
const inputData = req.body;
|
|
67
|
-
|
|
68
54
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
must: [
|
|
73
|
-
{
|
|
74
|
-
term: {
|
|
75
|
-
'store_id.keyword': inputData.storeId,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
term: {
|
|
80
|
-
'date_string': inputData.dateString,
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
|
|
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 = {
|
|
55
|
+
const config =await findOneStore( { storeId: inputData.storeId }, { revopTagging: 1 } );
|
|
56
|
+
if ( inputData.revopsType == 'employee' ) {
|
|
57
|
+
const getQuery = {
|
|
115
58
|
query: {
|
|
116
59
|
bool: {
|
|
117
60
|
must: [
|
|
@@ -130,51 +73,123 @@ export async function mappingConfig( req, res, next ) {
|
|
|
130
73
|
'revopsType.keyword': inputData.revopsType,
|
|
131
74
|
},
|
|
132
75
|
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const getData = await getOpenSearchCount( openSearch.revop, getQuery );
|
|
82
|
+
if ( getData && getData?.body?.count >= config?.revopTagging?.employee ) {
|
|
83
|
+
return res.sendError( `Select up to ${config?.revopTagging?.employee} items only`, 400 );
|
|
84
|
+
} else {
|
|
85
|
+
next();
|
|
86
|
+
}
|
|
87
|
+
} else if ( inputData.revopsType == 'house-keeping' ) {
|
|
88
|
+
const getQuery = {
|
|
89
|
+
query: {
|
|
90
|
+
bool: {
|
|
91
|
+
must: [
|
|
133
92
|
{
|
|
134
93
|
term: {
|
|
135
|
-
'
|
|
94
|
+
'storeId.keyword': inputData.storeId,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
term: {
|
|
99
|
+
'dateString': inputData.dateString,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
term: {
|
|
104
|
+
'revopsType.keyword': inputData.revopsType,
|
|
136
105
|
},
|
|
137
106
|
},
|
|
138
107
|
],
|
|
139
108
|
},
|
|
140
109
|
},
|
|
141
110
|
};
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
} );
|
|
111
|
+
const getData = await getOpenSearchCount( openSearch.revop, getQuery );
|
|
112
|
+
if ( getData && getData?.body?.count >= config?.revopTagging?.houseKeeping ) {
|
|
113
|
+
return res.sendError( `Select up to ${config?.revopTagging?.houseKeeping} items only`, 400 );
|
|
160
114
|
} else {
|
|
161
|
-
|
|
162
|
-
isLimitExceeded = taggedValue > limitValue;
|
|
163
|
-
logger.info( {
|
|
164
|
-
limitType: 'ABSOLUTE',
|
|
165
|
-
taggedValue,
|
|
166
|
-
limitValue,
|
|
167
|
-
isLimitExceeded,
|
|
168
|
-
forRevopsType: inputData.revopsType,
|
|
169
|
-
} );
|
|
115
|
+
next();
|
|
170
116
|
}
|
|
171
|
-
|
|
172
|
-
|
|
117
|
+
} else if ( inputData.revopsType === 'junk' ) {
|
|
118
|
+
if ( ( req?.user?.userType == 'client' && req?.user?.userType !== 'user ' ) || req?.user?.userType === 'tango' ) {
|
|
119
|
+
next();
|
|
120
|
+
return;
|
|
121
|
+
} else {
|
|
122
|
+
return res.sendError( 'Forbidden to junk mapping', 500 );
|
|
173
123
|
}
|
|
174
|
-
return next();
|
|
175
124
|
} else {
|
|
176
|
-
|
|
125
|
+
next();
|
|
177
126
|
}
|
|
127
|
+
// else if ( inputData.revopsType == 'duplicate' ) {
|
|
128
|
+
// const getFootfallQuery = {
|
|
129
|
+
// query: {
|
|
130
|
+
// terms: {
|
|
131
|
+
// _id: [ inputData?.dateString ],
|
|
132
|
+
// },
|
|
133
|
+
// },
|
|
134
|
+
// _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
135
|
+
// sort: [
|
|
136
|
+
// {
|
|
137
|
+
// date_iso: {
|
|
138
|
+
// order: 'desc',
|
|
139
|
+
// },
|
|
140
|
+
// },
|
|
141
|
+
// ],
|
|
142
|
+
// };
|
|
143
|
+
|
|
144
|
+
// const getFootfall = await getOpenSearchData( openSearch.footfall, getFootfallQuery );
|
|
145
|
+
// const footfall = getFootfall?.body?.hites?.hits?.[0]?._source?.footfall_count;
|
|
146
|
+
// const getQuery = {
|
|
147
|
+
// query: {
|
|
148
|
+
// bool: {
|
|
149
|
+
// must: [
|
|
150
|
+
// {
|
|
151
|
+
// term: {
|
|
152
|
+
// 'storeId.keyword': inputData.storeId,
|
|
153
|
+
// },
|
|
154
|
+
// },
|
|
155
|
+
// {
|
|
156
|
+
// term: {
|
|
157
|
+
// 'dateString': inputData.dateString,
|
|
158
|
+
// },
|
|
159
|
+
// },
|
|
160
|
+
// {
|
|
161
|
+
// term: {
|
|
162
|
+
// 'revopsType.keyword': inputData.revopsType,
|
|
163
|
+
// },
|
|
164
|
+
// },
|
|
165
|
+
// {
|
|
166
|
+
// term: {
|
|
167
|
+
// 'parent.keyword': null,
|
|
168
|
+
// },
|
|
169
|
+
// },
|
|
170
|
+
// ],
|
|
171
|
+
// },
|
|
172
|
+
// },
|
|
173
|
+
// };
|
|
174
|
+
// const getData = await getOpenSearchCount( openSearch.revop, getQuery );
|
|
175
|
+
// logger.info( { getData: getData, footfall: footfall, duplicate: config?.revopTagging?.duplicate } );
|
|
176
|
+
// if ( getData && footfall && config?.revopTagging?.duplicate ) {
|
|
177
|
+
// const data = config?.revopTagging?.duplicate;
|
|
178
|
+
// // Convert "20%" → 0.2 (handle both "20%" and 20)
|
|
179
|
+
// const percentStr = typeof data === 'string' ? data.replace( '%', '' ) : data;
|
|
180
|
+
// logger.info( { percentStr: percentStr } );
|
|
181
|
+
// const percentValue =percentStr / 100;
|
|
182
|
+
|
|
183
|
+
// const result = percentValue * footfall;
|
|
184
|
+
|
|
185
|
+
// logger.info( { result: result, footfall: footfall } );
|
|
186
|
+
// }
|
|
187
|
+
// if ( getData && getData?.body?.count >= Math.round( result ) ) {
|
|
188
|
+
// return res.sendError( `Select up to ${config?.revopTagging?.duplicate} items only`, 400 );
|
|
189
|
+
// } else {
|
|
190
|
+
// next();
|
|
191
|
+
// }
|
|
192
|
+
// }
|
|
178
193
|
} catch ( error ) {
|
|
179
194
|
logger.error( { error: error, message: req.body, function: 'traffic-revop-getTaggingConfig' } );
|
|
180
195
|
next();
|