tango-app-api-analysis-traffic 3.8.6 → 3.8.7-vms.1
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.7-vms.1",
|
|
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.23",
|
|
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,7 +2,8 @@ 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
|
|
5
|
+
import { findOneVmsStoreRequest } from '../services/vmsStoreRequest.service.js';
|
|
6
|
+
// import dayjs from 'dayjs';
|
|
6
7
|
// Lamda Service Call //
|
|
7
8
|
async function LamdaServiceCall( url, data ) {
|
|
8
9
|
try {
|
|
@@ -211,44 +212,135 @@ export async function storeProcessedData( req, res ) {
|
|
|
211
212
|
try {
|
|
212
213
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
213
214
|
const inputData = req.query;
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
215
|
+
const { fromDate, toDate, storeId } = inputData;
|
|
216
|
+
|
|
217
|
+
// Multi-date range handling for a single store
|
|
218
|
+
if ( fromDate && toDate && storeId ) {
|
|
219
|
+
const dayjs = ( await import( 'dayjs' ) ).default;
|
|
220
|
+
const isSameOrBefore = ( await import( 'dayjs/plugin/isSameOrBefore.js' ) ).default;
|
|
221
|
+
dayjs.extend( isSameOrBefore );
|
|
222
|
+
|
|
223
|
+
let start = dayjs( fromDate );
|
|
224
|
+
let end = dayjs( toDate );
|
|
225
|
+
|
|
226
|
+
if ( !start.isValid() || !end.isValid() ) {
|
|
227
|
+
return res.sendError( 'Invalid date range supplied', 400 );
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if ( end.isBefore( start ) ) {
|
|
231
|
+
[ start, end ] = [ end, start ];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const allDateStrings = [];
|
|
235
|
+
const orderedDates = [];
|
|
236
|
+
|
|
237
|
+
while ( start.isSameOrBefore( end ) ) {
|
|
238
|
+
const formatted = start.format( 'YYYY-MM-DD' );
|
|
239
|
+
orderedDates.push( formatted );
|
|
240
|
+
allDateStrings.push( `${storeId}_${formatted}` );
|
|
241
|
+
start = start.add( 1, 'day' );
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if ( allDateStrings.length === 0 ) {
|
|
245
|
+
return res.sendSuccess( [] );
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const footfallQuery = {
|
|
249
|
+
query: {
|
|
250
|
+
terms: {
|
|
251
|
+
_id: allDateStrings,
|
|
228
252
|
},
|
|
229
253
|
},
|
|
230
|
-
|
|
231
|
-
|
|
254
|
+
_source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
|
|
255
|
+
sort: [
|
|
256
|
+
{ date_iso: { order: 'asc' } },
|
|
257
|
+
],
|
|
258
|
+
size: allDateStrings.length,
|
|
259
|
+
};
|
|
232
260
|
|
|
233
|
-
|
|
234
|
-
|
|
261
|
+
const multiGet = await getOpenSearchData( openSearch.footfall, footfallQuery );
|
|
262
|
+
const multiHits = multiGet?.body?.hits?.hits || [];
|
|
263
|
+
const hitsMap = new Map();
|
|
264
|
+
multiHits.forEach( ( hit ) => {
|
|
265
|
+
hitsMap.set( hit?._id, hit?._source || null );
|
|
266
|
+
} );
|
|
235
267
|
|
|
236
|
-
|
|
237
|
-
const previousData = hits.find( ( d ) => d._id === dateStringPrevious )?._source || null;
|
|
268
|
+
const responseArray = [];
|
|
238
269
|
|
|
239
|
-
|
|
270
|
+
for ( let i = 0; i < orderedDates.length; i++ ) {
|
|
271
|
+
const currentDate = orderedDates[i];
|
|
272
|
+
const currentId = `${storeId}_${currentDate}`;
|
|
273
|
+
const processedData = hitsMap.get( currentId );
|
|
274
|
+
if ( !processedData ) {
|
|
275
|
+
responseArray.push( {
|
|
276
|
+
date: currentDate,
|
|
277
|
+
footfallCount: 0,
|
|
278
|
+
footfallCountTrend: 0,
|
|
279
|
+
downtime: 0,
|
|
280
|
+
} );
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
240
283
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
284
|
+
const prevDate = dayjs( currentDate ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
|
|
285
|
+
const prevId = `${storeId}_${prevDate}`;
|
|
286
|
+
const previousData = hitsMap.get( prevId );
|
|
287
|
+
|
|
288
|
+
let footfallCountTrend = 0;
|
|
289
|
+
if ( previousData && previousData.footfall_count ) {
|
|
290
|
+
footfallCountTrend = Math.round(
|
|
291
|
+
( ( processedData.footfall_count - previousData.footfall_count ) / previousData.footfall_count ) * 100,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
// Add ticket status from openSearch.footfallDirectory (_source.status)
|
|
295
|
+
let ticketStatus = null;
|
|
296
|
+
// Try to find a matching footfallDirectory record for this date+storeId
|
|
297
|
+
// const ticketKey = `${storeId}_${currentDate}`;
|
|
298
|
+
const footfallDirQuery = {
|
|
299
|
+
query: {
|
|
300
|
+
bool: {
|
|
301
|
+
must: [
|
|
302
|
+
{ term: { 'storeId.keyword': storeId } },
|
|
303
|
+
{ term: { 'dateString': currentDate } },
|
|
304
|
+
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
size: 1,
|
|
309
|
+
_source: [ 'status' ],
|
|
310
|
+
};
|
|
311
|
+
try {
|
|
312
|
+
const footfallDirRes = await getOpenSearchData( openSearch.footfallDirectory, footfallDirQuery );
|
|
313
|
+
const hit = footfallDirRes?.body?.hits?.hits?.[0];
|
|
314
|
+
ticketStatus = hit?._source?.status || null;
|
|
315
|
+
} catch ( err ) {
|
|
316
|
+
logger.warn( { message: 'Could not get ticket status from footfallDirectory', error: err } );
|
|
317
|
+
}
|
|
318
|
+
// Check if request status ("raised") should be enabled or disabled by querying MongoDB config
|
|
319
|
+
// We'll assume findOnerevopConfig can fetch the record with status for this storeId and dateString
|
|
320
|
+
let raisedStatusEnabled = 'reset'; // default: enabled
|
|
321
|
+
try {
|
|
322
|
+
const mongoConfig = await findOneVmsStoreRequest( { storeId: storeId, dateString: currentDate } );
|
|
323
|
+
if ( mongoConfig && mongoConfig.status ) {
|
|
324
|
+
raisedStatusEnabled = mongoConfig.status;
|
|
325
|
+
}
|
|
326
|
+
} catch ( err ) {
|
|
327
|
+
logger.warn( { message: 'Could not get request status from MongoDB', error: err } );
|
|
328
|
+
// Leave raisedStatusEnabled as default
|
|
329
|
+
}
|
|
246
330
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
331
|
+
responseArray.push( {
|
|
332
|
+
date: processedData?.date_string || currentDate,
|
|
333
|
+
footfallCount: processedData?.footfall_count || 0,
|
|
334
|
+
footfallCountTrend,
|
|
335
|
+
downtime: processedData?.down_time || 0,
|
|
336
|
+
ticketStatus,
|
|
337
|
+
raisedStatusEnabled,
|
|
338
|
+
} );
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return res.sendSuccess( responseArray );
|
|
342
|
+
}
|
|
343
|
+
return res.sendError( 'Required parameters missing', 400 );
|
|
252
344
|
} catch ( error ) {
|
|
253
345
|
logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
|
|
254
346
|
const err = error.message || 'Internal Server Error';
|
|
@@ -336,6 +428,7 @@ export async function tagTempId( req, res ) {
|
|
|
336
428
|
clientId: inputData.storeId.split( '-' )[0],
|
|
337
429
|
storeId: inputData.storeId,
|
|
338
430
|
tempId: inputData.tempId,
|
|
431
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
339
432
|
dateString: inputData.dateString,
|
|
340
433
|
timeRange: inputData.timeRange,
|
|
341
434
|
processType: inputData.processType,
|
|
@@ -348,6 +441,14 @@ export async function tagTempId( req, res ) {
|
|
|
348
441
|
isChecked: inputData.isChecked,
|
|
349
442
|
duplicateImage: inputData?.duplicateImage?.length>0? inputData?.duplicateImage :[],
|
|
350
443
|
type: 'tagging-reflect',
|
|
444
|
+
ticketStatus: 'submitted',
|
|
445
|
+
isParent: inputData?.revopsType === 'duplicate'? true : false,
|
|
446
|
+
actions: [
|
|
447
|
+
{
|
|
448
|
+
actionType: 'tagging',
|
|
449
|
+
action: 'submitted',
|
|
450
|
+
},
|
|
451
|
+
],
|
|
351
452
|
createdAt: new Date(),
|
|
352
453
|
updatedAt: new Date(),
|
|
353
454
|
|
|
@@ -386,6 +487,7 @@ export async function tagTempId( req, res ) {
|
|
|
386
487
|
clientId: inputData.storeId.split( '-' )[0],
|
|
387
488
|
storeId: inputData.storeId,
|
|
388
489
|
tempId: item.tempId,
|
|
490
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
389
491
|
dateString: inputData.dateString,
|
|
390
492
|
timeRange: item.timeRange,
|
|
391
493
|
isChecked: item.isChecked,
|
|
@@ -397,7 +499,15 @@ export async function tagTempId( req, res ) {
|
|
|
397
499
|
status: item?.revopsType == 'non-tagging' ?'':'submitted',
|
|
398
500
|
description: '',
|
|
399
501
|
duplicateImage: [],
|
|
502
|
+
isParent: false,
|
|
400
503
|
type: 'tagging-reflect',
|
|
504
|
+
ticketStatus: 'submitted',
|
|
505
|
+
actions: [
|
|
506
|
+
{
|
|
507
|
+
actionType: 'tagging',
|
|
508
|
+
action: 'submitted',
|
|
509
|
+
},
|
|
510
|
+
],
|
|
401
511
|
createdAt: new Date(),
|
|
402
512
|
updatedAt: new Date(),
|
|
403
513
|
|
package/src/dtos/revop.dtos.js
CHANGED