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.6",
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.9",
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 dayjs from 'dayjs';
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 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 ],
221
- },
222
- },
223
- _source: [ 'footfall', 'date_string', 'store_id', 'down_time', 'footfall_count' ],
224
- sort: [
225
- {
226
- date_iso: {
227
- order: 'desc',
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
- const getData = await getOpenSearchData( openSearch.footfall, getQuery );
234
- const hits = getData?.body?.hits?.hits || [];
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
- const processedData = hits.find( ( d ) => d._id === dateString )?._source || null;
237
- const previousData = hits.find( ( d ) => d._id === dateStringPrevious )?._source || null;
268
+ const responseArray = [];
238
269
 
239
- let footfallCountTrend = 0;
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
- if ( processedData && previousData && previousData.footfall_count ) {
242
- footfallCountTrend = Math.round(
243
- ( ( processedData.footfall_count - previousData.footfall_count ) / previousData.footfall_count ) * 100,
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
- return res.sendSuccess( {
248
- footfallCount: processedData?.footfall_count || 0,
249
- footfallCountTrend,
250
- downtime: processedData?.down_time || 0,
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
 
@@ -4,7 +4,8 @@ import dayjs from 'dayjs';
4
4
  export const storeProcessedDataSchema = joi.object( {
5
5
 
6
6
  storeId: joi.string().required(),
7
- dateString: joi.string().required(),
7
+ fromDate: joi.string().required(),
8
+ toDate: joi.string().required(),
8
9
 
9
10
  } );
10
11
 
@@ -0,0 +1,5 @@
1
+ import vmsStoreRequestModel from 'tango-api-schema/schema/vmsStoreRequest.model.js';
2
+
3
+ export async function findOneVmsStoreRequest( query = {} ) {
4
+ return await vmsStoreRequestModel.findOne( query );
5
+ };