tango-app-api-analysis-traffic 3.8.7-vms.3 → 3.8.7-vms.30
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 +2 -2
- package/src/controllers/revop.controller.js +679 -38
- package/src/controllers/tangoTrafficV3.controllers.js +27 -26
- package/src/dtos/revop.dtos.js +13 -2
- package/src/dtos/validation.dtos.js +11 -0
- package/src/routes/revop.routes.js +5 -5
- package/src/routes/traffic.routes.js +3 -1
- package/src/validations/revop.validation.js +229 -111
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger, insertOpenSearchData, getOpenSearchData, updateOpenSearchData } from 'tango-app-api-middleware';
|
|
2
2
|
import { findOnerevopConfig } from '../services/revopConfig.service.js';
|
|
3
3
|
import * as clientService from '../services/clients.services.js';
|
|
4
|
-
import { bulkUpdate, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
4
|
+
import { bulkUpdate, insertWithId, upsertWithScript } from 'tango-app-api-middleware/src/utils/openSearch.js';
|
|
5
5
|
import { findOneVmsStoreRequest } from '../services/vmsStoreRequest.service.js';
|
|
6
6
|
// import dayjs from 'dayjs';
|
|
7
7
|
// Lamda Service Call //
|
|
@@ -137,6 +137,109 @@ export async function getrevoptagging( req, res ) {
|
|
|
137
137
|
return res.sendError( { error: error }, 500 );
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
export async function migrateRevopIndex( req, res ) {
|
|
142
|
+
try {
|
|
143
|
+
const { storeId, dateString, size = 500 } = req.body;
|
|
144
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
145
|
+
|
|
146
|
+
const query = {
|
|
147
|
+
size: size,
|
|
148
|
+
query: {
|
|
149
|
+
bool: {
|
|
150
|
+
must: [],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if ( storeId ) {
|
|
156
|
+
query.query.bool.must.push( {
|
|
157
|
+
term: {
|
|
158
|
+
'storeId.keyword': storeId,
|
|
159
|
+
},
|
|
160
|
+
} );
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if ( dateString ) {
|
|
164
|
+
query.query.bool.must.push( {
|
|
165
|
+
terms: {
|
|
166
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
167
|
+
},
|
|
168
|
+
} );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const response = await getOpenSearchData( openSearch.revop, query );
|
|
172
|
+
const hits = response?.body?.hits?.hits || [];
|
|
173
|
+
|
|
174
|
+
if ( hits.length === 0 ) {
|
|
175
|
+
return res.sendSuccess( { message: 'No records found for migration', updated: 0 } );
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const bulkBody = [];
|
|
179
|
+
|
|
180
|
+
for ( const hit of hits ) {
|
|
181
|
+
const src = hit._source || {};
|
|
182
|
+
const statusValue = ( src.status || '' ).toLowerCase();
|
|
183
|
+
const parentValue = src.parent;
|
|
184
|
+
const idValue = `${src.storeId || ''}_${src.dateString || src.dteString || ''}_${src.tempId || ''}`;
|
|
185
|
+
|
|
186
|
+
let actions = [
|
|
187
|
+
{
|
|
188
|
+
actionType: 'tagging',
|
|
189
|
+
action: 'submitted',
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
if ( statusValue === 'approved' ) {
|
|
194
|
+
actions = [
|
|
195
|
+
{
|
|
196
|
+
actionType: 'tagging',
|
|
197
|
+
action: 'submitted',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
actionType: 'review',
|
|
201
|
+
action: 'approved',
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
} else if ( statusValue === 'rejected' ) {
|
|
205
|
+
actions = [
|
|
206
|
+
{
|
|
207
|
+
actionType: 'tagging',
|
|
208
|
+
action: 'submitted',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
actionType: 'review',
|
|
212
|
+
action: 'rejected',
|
|
213
|
+
},
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const doc = {
|
|
218
|
+
id: idValue,
|
|
219
|
+
isParent: parentValue === null || parentValue === undefined ? false : true,
|
|
220
|
+
actions,
|
|
221
|
+
ticketStatus: src.status,
|
|
222
|
+
// updatedAt: new Date(),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
bulkBody.push(
|
|
226
|
+
{ update: { _index: openSearch.newRevop, _id: hit._id } },
|
|
227
|
+
{ doc: doc, doc_as_upsert: true },
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
232
|
+
if ( bulkRes?.errors ) {
|
|
233
|
+
logger.error( 'Bulk migration errors:', bulkRes.items );
|
|
234
|
+
return res.sendError( 'Failed to migrate some records', 500 );
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return res.sendSuccess( { message: 'Migration completed', updated: hits.length } );
|
|
238
|
+
} catch ( error ) {
|
|
239
|
+
logger.error( { error: error, message: req.body, function: 'migrateRevopIndex' } );
|
|
240
|
+
return res.sendError( { error: error }, 500 );
|
|
241
|
+
}
|
|
242
|
+
}
|
|
140
243
|
export async function revoptaggingcount( req, res ) {
|
|
141
244
|
try {
|
|
142
245
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
@@ -221,6 +324,9 @@ export async function storeProcessedData( req, res ) {
|
|
|
221
324
|
dayjs.extend( isSameOrBefore );
|
|
222
325
|
|
|
223
326
|
let start = dayjs( fromDate );
|
|
327
|
+
// get start value from the before day one
|
|
328
|
+
// Move start one day back so we can access "day before" when looping
|
|
329
|
+
start = start.subtract( 1, 'day' );
|
|
224
330
|
let end = dayjs( toDate );
|
|
225
331
|
|
|
226
332
|
if ( !start.isValid() || !end.isValid() ) {
|
|
@@ -267,10 +373,12 @@ export async function storeProcessedData( req, res ) {
|
|
|
267
373
|
|
|
268
374
|
const responseArray = [];
|
|
269
375
|
|
|
270
|
-
for ( let i =
|
|
376
|
+
for ( let i = 1; i < orderedDates.length; i++ ) {
|
|
271
377
|
const currentDate = orderedDates[i];
|
|
272
378
|
const currentId = `${storeId}_${currentDate}`;
|
|
379
|
+
logger.info( { currentId, currentDate } );
|
|
273
380
|
const processedData = hitsMap.get( currentId );
|
|
381
|
+
logger.info( { processedData } );
|
|
274
382
|
if ( !processedData ) {
|
|
275
383
|
responseArray.push( {
|
|
276
384
|
date: currentDate,
|
|
@@ -283,13 +391,17 @@ export async function storeProcessedData( req, res ) {
|
|
|
283
391
|
|
|
284
392
|
const prevDate = dayjs( currentDate ).subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
|
|
285
393
|
const prevId = `${storeId}_${prevDate}`;
|
|
394
|
+
logger.info( { prevId, prevDate } );
|
|
286
395
|
const previousData = hitsMap.get( prevId );
|
|
287
396
|
|
|
288
397
|
let footfallCountTrend = 0;
|
|
398
|
+
logger.info( { previousData, previoucubr1: previousData?.footfall_count } );
|
|
289
399
|
if ( previousData && previousData.footfall_count ) {
|
|
400
|
+
logger.info( { previousData, previoucubr: previousData?.footfall_count } );
|
|
290
401
|
footfallCountTrend = Math.round(
|
|
291
|
-
( ( processedData.footfall_count - previousData
|
|
402
|
+
( ( processedData.footfall_count - previousData?.footfall_count ) / previousData.footfall_count ) * 100,
|
|
292
403
|
);
|
|
404
|
+
logger.info( { footfallCountTrend } );
|
|
293
405
|
}
|
|
294
406
|
// Add ticket status from openSearch.footfallDirectory (_source.status)
|
|
295
407
|
let ticketStatus = null;
|
|
@@ -302,6 +414,11 @@ export async function storeProcessedData( req, res ) {
|
|
|
302
414
|
{ term: { 'storeId.keyword': storeId } },
|
|
303
415
|
{ term: { 'dateString': currentDate } },
|
|
304
416
|
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
417
|
+
{
|
|
418
|
+
'term': {
|
|
419
|
+
'type.keyword': 'store',
|
|
420
|
+
},
|
|
421
|
+
},
|
|
305
422
|
],
|
|
306
423
|
},
|
|
307
424
|
},
|
|
@@ -403,43 +520,177 @@ export async function footFallImages( req, res ) {
|
|
|
403
520
|
'ticketName.keyword': 'footfall-directory',
|
|
404
521
|
},
|
|
405
522
|
},
|
|
523
|
+
{
|
|
524
|
+
'term': {
|
|
525
|
+
'type.keyword': 'store',
|
|
526
|
+
},
|
|
527
|
+
},
|
|
406
528
|
],
|
|
407
529
|
},
|
|
408
530
|
},
|
|
409
|
-
|
|
531
|
+
'_source': [ 'dateString', 'storeId', 'mappingInfo', 'revicedFootfall', 'revicedPerc', 'reviced', 'createdAt', 'updatedAt', 'footfallCount' ],
|
|
410
532
|
|
|
411
533
|
};
|
|
412
534
|
|
|
413
535
|
const getData = await getOpenSearchData( opensearch.footfallDirectory, query );
|
|
414
536
|
const ticketDetails = getData?.body?.hits?.hits[0];
|
|
415
537
|
let temp = [];
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
538
|
+
const footfallValue = ticketDetails?._source?.footfallCount ?? 0;
|
|
539
|
+
const mappingInfoArray = ticketDetails?._source?.mappingInfo ?? [];
|
|
540
|
+
// Helper to get mappingInfo for an actionType
|
|
541
|
+
function getMappingForType( type ) {
|
|
542
|
+
return mappingInfoArray.find( ( m ) => m.type === type ) || {};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// List of actionTypes to process in sequence
|
|
546
|
+
|
|
547
|
+
if ( req.user.userType !== 'tango' && req.user.role !== 'superadmin' ) {
|
|
548
|
+
switch ( req.user.role ) {
|
|
549
|
+
case 'user':
|
|
550
|
+
const actionTypesUser = [ 'tagging', 'finalRevision' ];
|
|
551
|
+
|
|
552
|
+
temp = [];
|
|
553
|
+
actionTypesUser.forEach( ( type ) => {
|
|
554
|
+
const mapping = getMappingForType( type );
|
|
555
|
+
if ( type === 'tagging' ) {
|
|
556
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
557
|
+
const revisedPerc =
|
|
558
|
+
footfallValue > 0 ?
|
|
559
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
560
|
+
'0';
|
|
561
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
562
|
+
temp.push( {
|
|
563
|
+
actionType: type,
|
|
564
|
+
footfall: footfallValue,
|
|
565
|
+
revicedFootfall: revisedFootfall,
|
|
566
|
+
revicedPerc: revisedPerc,
|
|
567
|
+
count: countObj,
|
|
568
|
+
createdAt: mapping.createdAt ?? '',
|
|
569
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
570
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
571
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
572
|
+
isUp: false,
|
|
573
|
+
} );
|
|
574
|
+
} else if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
575
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
576
|
+
const revisedPerc =
|
|
577
|
+
footfallValue > 0 ?
|
|
578
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
579
|
+
'0';
|
|
580
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
581
|
+
temp.push( {
|
|
582
|
+
actionType: type,
|
|
583
|
+
footfall: footfallValue,
|
|
584
|
+
revicedFootfall: revisedFootfall,
|
|
585
|
+
revicedPerc: revisedPerc,
|
|
586
|
+
count: countObj,
|
|
587
|
+
createdAt: mapping.createdAt ?? '',
|
|
588
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
589
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
590
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
591
|
+
} );
|
|
592
|
+
}
|
|
593
|
+
} );
|
|
594
|
+
break;
|
|
595
|
+
case 'admin':
|
|
596
|
+
const actionTypesAdmin = [ 'tagging', 'review', 'finalRevision' ];
|
|
597
|
+
temp = [];
|
|
598
|
+
actionTypesAdmin.forEach( ( type ) => {
|
|
599
|
+
const mapping = getMappingForType( type );
|
|
600
|
+
if ( type === 'tagging' ) {
|
|
601
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
602
|
+
const revisedPerc =
|
|
603
|
+
footfallValue > 0 ?
|
|
604
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
605
|
+
'0';
|
|
606
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
607
|
+
temp.push( {
|
|
608
|
+
actionType: type,
|
|
609
|
+
footfall: footfallValue,
|
|
610
|
+
revicedFootfall: revisedFootfall,
|
|
611
|
+
revicedPerc: revisedPerc,
|
|
612
|
+
count: countObj,
|
|
613
|
+
createdAt: mapping.createdAt ?? '',
|
|
614
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
615
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
616
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
617
|
+
isUp: false,
|
|
618
|
+
} );
|
|
619
|
+
} else if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
620
|
+
const revisedFootfall = mapping.revicedFootfall ?? 0;
|
|
621
|
+
const revisedPerc =
|
|
622
|
+
footfallValue > 0 ?
|
|
623
|
+
`${Math.round( ( revisedFootfall / footfallValue ) * 100 )}` :
|
|
624
|
+
'0';
|
|
625
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
626
|
+
temp.push( {
|
|
627
|
+
actionType: type,
|
|
628
|
+
footfall: footfallValue,
|
|
629
|
+
revicedFootfall: revisedFootfall,
|
|
630
|
+
revicedPerc: revisedPerc,
|
|
631
|
+
count: countObj,
|
|
632
|
+
createdAt: mapping.createdAt ?? '',
|
|
633
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
634
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
635
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
636
|
+
} );
|
|
637
|
+
}
|
|
638
|
+
} );
|
|
639
|
+
}
|
|
640
|
+
} else {
|
|
641
|
+
const actionTypes = [ 'tagging', 'review', 'approve', 'tangoreview', 'finalRevision' ];
|
|
642
|
+
|
|
643
|
+
// Dynamically add to temp only if actionType matches and status is 'closed',
|
|
644
|
+
// except for 'tagging' where status must be 'raised'
|
|
645
|
+
temp = [];
|
|
646
|
+
actionTypes.forEach( ( type ) => {
|
|
647
|
+
const mapping = getMappingForType( type );
|
|
648
|
+
if ( type === 'tagging' ) {
|
|
649
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
650
|
+
temp.push( {
|
|
651
|
+
actionType: type,
|
|
652
|
+
footfall: footfallValue,
|
|
653
|
+
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
654
|
+
revicedPerc: mapping.reviced ?? '--',
|
|
655
|
+
count: countObj,
|
|
656
|
+
createdAt: mapping.createdAt ?? '',
|
|
657
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
658
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
659
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
660
|
+
isUp: false,
|
|
661
|
+
} );
|
|
662
|
+
}
|
|
663
|
+
if ( type !== 'tagging' && mapping.status === 'Closed' ) {
|
|
664
|
+
const countObj = mapping.count ? [ ...mapping.count ] : [];
|
|
665
|
+
temp.push( {
|
|
666
|
+
actionType: type,
|
|
667
|
+
footfall: footfallValue,
|
|
668
|
+
revicedFootfall: mapping.revicedFootfall ?? 0,
|
|
669
|
+
revicedPerc: mapping.reviced ?? '--',
|
|
670
|
+
count: countObj,
|
|
671
|
+
createdAt: mapping.createdAt ?? '',
|
|
672
|
+
createdByEmail: mapping.createdByEmail ?? '',
|
|
673
|
+
createdByUserName: mapping.createdByUserName ?? '',
|
|
674
|
+
createdByRole: mapping.createdByRole ?? '',
|
|
675
|
+
} );
|
|
676
|
+
}
|
|
677
|
+
} );
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
const LamdaURL = revop.getImages;
|
|
682
|
+
let resultData = await LamdaServiceCall( LamdaURL, inputData );
|
|
683
|
+
if ( resultData ) {
|
|
684
|
+
// temp.length? temp[0].status = 'open': null;
|
|
685
|
+
|
|
686
|
+
if ( resultData.status_code == '200' ) {
|
|
687
|
+
return res.sendSuccess( { ...resultData, ticketStatus: temp?.length > 0 && ticketDetails? temp : null, config: req?.store?.footfallDirectoryConfigs } );
|
|
688
|
+
} else {
|
|
689
|
+
return res.sendError( 'No Content', 204 );
|
|
690
|
+
}
|
|
691
|
+
} else {
|
|
692
|
+
return res.sendError( 'No Content', 204 );
|
|
693
|
+
}
|
|
443
694
|
} catch ( error ) {
|
|
444
695
|
logger.error( { message: error, data: req.query, function: 'storeProcessedData' } );
|
|
445
696
|
const err = error.message || 'Internal Server Error';
|
|
@@ -464,9 +715,21 @@ export async function tagTempId( req, res ) {
|
|
|
464
715
|
exitTime: inputData.exitTime,
|
|
465
716
|
filePath: inputData.filePath,
|
|
466
717
|
status: inputData?.revopsType == 'non-tagging' ?'':'submitted',
|
|
467
|
-
description: '',
|
|
468
|
-
isChecked:
|
|
469
|
-
|
|
718
|
+
description: inputData.comments || '',
|
|
719
|
+
isChecked: null,
|
|
720
|
+
// Add id to each object in duplicateImage if it exists and is an array
|
|
721
|
+
duplicateImage: Array.isArray( inputData?.duplicateImage ) ?
|
|
722
|
+
inputData.duplicateImage.map( ( img ) => ( {
|
|
723
|
+
...img,
|
|
724
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${img?.tempId}`,
|
|
725
|
+
actions: [
|
|
726
|
+
{
|
|
727
|
+
actionType: 'tagging',
|
|
728
|
+
action: 'submitted',
|
|
729
|
+
},
|
|
730
|
+
],
|
|
731
|
+
} ) ) :
|
|
732
|
+
[],
|
|
470
733
|
type: 'tagging-reflect',
|
|
471
734
|
ticketStatus: 'submitted',
|
|
472
735
|
isParent: inputData?.revopsType === 'duplicate'? true : false,
|
|
@@ -476,6 +739,7 @@ export async function tagTempId( req, res ) {
|
|
|
476
739
|
action: 'submitted',
|
|
477
740
|
},
|
|
478
741
|
],
|
|
742
|
+
comments: inputData.comments || '',
|
|
479
743
|
createdAt: new Date(),
|
|
480
744
|
updatedAt: new Date(),
|
|
481
745
|
|
|
@@ -491,8 +755,10 @@ export async function tagTempId( req, res ) {
|
|
|
491
755
|
ctx._source.description = params.description;
|
|
492
756
|
ctx._source.isChecked = params.isChecked;
|
|
493
757
|
ctx._source.duplicateImage = params.duplicateImage;
|
|
758
|
+
ctx._source.isParent = params.isParent;
|
|
494
759
|
ctx._source.updatedAt = params.updatedAt;
|
|
495
760
|
ctx._source.timeRange = params.timeRange;
|
|
761
|
+
ctx._source.comments = params.comments;
|
|
496
762
|
if (ctx._source.createdAt == null) {
|
|
497
763
|
ctx._source.createdAt = params.createdAt;
|
|
498
764
|
ctx._source.clientId = params.clientId;
|
|
@@ -507,6 +773,32 @@ export async function tagTempId( req, res ) {
|
|
|
507
773
|
};
|
|
508
774
|
const id = `${inputData.storeId}_${inputData.dateString}_${inputData.timeRange}_${inputData.tempId}`;
|
|
509
775
|
await upsertWithScript( openSearch.revop, id, { script, upsert: upsertRecord } );
|
|
776
|
+
if ( inputData?.comments && inputData?.comments !== '' ) {
|
|
777
|
+
const id = `${inputData.storeId}_${inputData.dateString}_${Date.now()}`;
|
|
778
|
+
const logs = {
|
|
779
|
+
type: 'tagging',
|
|
780
|
+
parent: inputData?.revopsType == 'duplicate'? inputData.tempId : null,
|
|
781
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${inputData?.tempId}`,
|
|
782
|
+
tempId: inputData.tempId,
|
|
783
|
+
timeRange: inputData.timeRange,
|
|
784
|
+
storeId: inputData.storeId,
|
|
785
|
+
dateString: inputData.dateString,
|
|
786
|
+
processType: inputData.processType,
|
|
787
|
+
category: inputData.revopsType,
|
|
788
|
+
entryTime: inputData.entryTime,
|
|
789
|
+
exitTime: inputData.exitTime,
|
|
790
|
+
filePath: inputData.filePath,
|
|
791
|
+
status: inputData?.revopsType == 'non-tagging' ?'':'submitted',
|
|
792
|
+
description: inputData.comments || '',
|
|
793
|
+
isChecked: null,
|
|
794
|
+
createdByEmail: req?.user?.email,
|
|
795
|
+
createdByUserName: req?.user?.userName,
|
|
796
|
+
createdByRole: req?.user?.role,
|
|
797
|
+
message: inputData.comments || '',
|
|
798
|
+
createdAt: new Date(),
|
|
799
|
+
};
|
|
800
|
+
await insertWithId( openSearch.vmsCommentsLog, id, logs );
|
|
801
|
+
}
|
|
510
802
|
if ( inputData?.duplicateImage?.length> 0 ) {
|
|
511
803
|
let bulkBody = [];
|
|
512
804
|
for ( let item of inputData?.duplicateImage ) {
|
|
@@ -514,10 +806,10 @@ export async function tagTempId( req, res ) {
|
|
|
514
806
|
clientId: inputData.storeId.split( '-' )[0],
|
|
515
807
|
storeId: inputData.storeId,
|
|
516
808
|
tempId: item.tempId,
|
|
517
|
-
id: `${inputData?.storeId}_${inputData?.dateString}_${
|
|
809
|
+
id: `${inputData?.storeId}_${inputData?.dateString}_${item?.tempId}`,
|
|
518
810
|
dateString: inputData.dateString,
|
|
519
811
|
timeRange: item.timeRange,
|
|
520
|
-
isChecked:
|
|
812
|
+
isChecked: null,
|
|
521
813
|
processType: inputData.processType,
|
|
522
814
|
revopsType: item.revopsType,
|
|
523
815
|
entryTime: item.entryTime,
|
|
@@ -529,6 +821,7 @@ export async function tagTempId( req, res ) {
|
|
|
529
821
|
isParent: false,
|
|
530
822
|
type: 'tagging-reflect',
|
|
531
823
|
ticketStatus: 'submitted',
|
|
824
|
+
comments: inputData.comments || '',
|
|
532
825
|
actions: [
|
|
533
826
|
{
|
|
534
827
|
actionType: 'tagging',
|
|
@@ -556,12 +849,20 @@ export async function tagTempId( req, res ) {
|
|
|
556
849
|
return { success: false, errors: res1.items };
|
|
557
850
|
} else {
|
|
558
851
|
logger.info( { msg: 'res1' } );
|
|
559
|
-
return res.sendSuccess( `ID tagged as
|
|
852
|
+
return res.sendSuccess( `ID tagged as Duplicates` );
|
|
560
853
|
// return { success: true };
|
|
561
854
|
}
|
|
562
855
|
}
|
|
563
856
|
} else {
|
|
564
|
-
|
|
857
|
+
// Convert camelCase revopsType to space-separated and capitalize first letter of each word
|
|
858
|
+
function camelCaseToTitle( str ) {
|
|
859
|
+
if ( !str ) return '';
|
|
860
|
+
return str.replace( /([A-Z])/g, ' $1' ).replace( /^./, ( s ) => s.toUpperCase() );
|
|
861
|
+
}
|
|
862
|
+
const titleRevopsType = camelCaseToTitle( inputData?.revopsType );
|
|
863
|
+
const message = inputData?.revopsType == 'non-tagging' ?
|
|
864
|
+
'ID removed from tagging' :
|
|
865
|
+
`ID tagged as ${titleRevopsType}`;
|
|
565
866
|
return res.sendSuccess( message );
|
|
566
867
|
}
|
|
567
868
|
} catch ( error ) {
|
|
@@ -588,3 +889,343 @@ export async function getCategorizedImages( req, res ) {
|
|
|
588
889
|
return res.sendError( err, 500 );
|
|
589
890
|
}
|
|
590
891
|
}
|
|
892
|
+
|
|
893
|
+
export async function vmsDataMigration( req, res ) {
|
|
894
|
+
try {
|
|
895
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
896
|
+
const inputData = req.body;
|
|
897
|
+
const { storeId, dateString, limit = 100 } = inputData;
|
|
898
|
+
|
|
899
|
+
// Build query to fetch old structure documents
|
|
900
|
+
const query = {
|
|
901
|
+
query: {
|
|
902
|
+
bool: {
|
|
903
|
+
must: [
|
|
904
|
+
{
|
|
905
|
+
term: {
|
|
906
|
+
'ticketName.keyword': 'footfall-directory',
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
],
|
|
910
|
+
},
|
|
911
|
+
},
|
|
912
|
+
size: parseInt( limit ),
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// Add storeId filter if provided
|
|
916
|
+
if ( storeId ) {
|
|
917
|
+
query.query.bool.must.push( {
|
|
918
|
+
term: {
|
|
919
|
+
'storeId.keyword': storeId,
|
|
920
|
+
},
|
|
921
|
+
} );
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Add dateString filter if provided
|
|
925
|
+
if ( dateString ) {
|
|
926
|
+
query.query.bool.must.push( {
|
|
927
|
+
terms: {
|
|
928
|
+
dateString: dateString?.split( ',' ),
|
|
929
|
+
},
|
|
930
|
+
} );
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Exclude documents that already have the new structure (have mappingInfo or type: 'store')
|
|
934
|
+
query.query.bool.must_not = [
|
|
935
|
+
{
|
|
936
|
+
exists: {
|
|
937
|
+
field: 'mappingInfo',
|
|
938
|
+
},
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
term: {
|
|
942
|
+
'type.keyword': 'store',
|
|
943
|
+
},
|
|
944
|
+
},
|
|
945
|
+
];
|
|
946
|
+
|
|
947
|
+
const getData = await getOpenSearchData( openSearch.oldFootfallDirectory, query );
|
|
948
|
+
logger.info( { getData } );
|
|
949
|
+
const hits = getData?.body?.hits?.hits || [];
|
|
950
|
+
|
|
951
|
+
if ( hits.length === 0 ) {
|
|
952
|
+
return res.sendSuccess( { message: 'No documents found to migrate', migrated: 0 } );
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
let migratedCount = 0;
|
|
956
|
+
const errors = [];
|
|
957
|
+
|
|
958
|
+
for ( const hit of hits ) {
|
|
959
|
+
try {
|
|
960
|
+
const oldSource = hit._source;
|
|
961
|
+
const documentId = hit._id;
|
|
962
|
+
|
|
963
|
+
// Calculate revicedFootfall (sum of AC counts)
|
|
964
|
+
const revicedFootfall = ( oldSource.duplicateACCount || 0 ) +
|
|
965
|
+
( oldSource.employeeACCount || 0 ) +
|
|
966
|
+
( oldSource.houseKeepingACCount || 0 ) +
|
|
967
|
+
( oldSource.junkACCount || 0 );
|
|
968
|
+
|
|
969
|
+
// Calculate revicedPerc
|
|
970
|
+
const footfallCount = oldSource.footfallCount || 0;
|
|
971
|
+
const revicedPerc = footfallCount > 0 ?
|
|
972
|
+
Math.round( ( revicedFootfall / footfallCount ) * 100 ) :
|
|
973
|
+
0;
|
|
974
|
+
|
|
975
|
+
// Calculate reviced
|
|
976
|
+
const reviced = parseInt( revicedPerc );
|
|
977
|
+
|
|
978
|
+
// Transform arrays to revisedDetail format
|
|
979
|
+
const revisedDetail = [];
|
|
980
|
+
|
|
981
|
+
// Transform duplicateImages
|
|
982
|
+
if ( Array.isArray( oldSource.duplicateImages ) ) {
|
|
983
|
+
for ( const duplicate of oldSource.duplicateImages ) {
|
|
984
|
+
const parentId = `${oldSource.storeId}_${oldSource.dateString}_${duplicate.tempId}`;
|
|
985
|
+
const parentDetail = {
|
|
986
|
+
id: parentId,
|
|
987
|
+
clientId: oldSource.clientId,
|
|
988
|
+
storeId: oldSource.storeId,
|
|
989
|
+
tempId: duplicate.tempId,
|
|
990
|
+
dateString: oldSource.dateString,
|
|
991
|
+
timeRange: duplicate.timeRange,
|
|
992
|
+
processType: 'footfall',
|
|
993
|
+
revopsType: 'duplicate',
|
|
994
|
+
entryTime: duplicate.entryTime,
|
|
995
|
+
exitTime: duplicate.exitTime,
|
|
996
|
+
filePath: duplicate.filePath,
|
|
997
|
+
status: oldSource.duplicateStatus || 'submitted',
|
|
998
|
+
description: '',
|
|
999
|
+
isChecked: duplicate.isChecked !== undefined ? duplicate.isChecked : false,
|
|
1000
|
+
type: 'tagging-reflect',
|
|
1001
|
+
parent: null,
|
|
1002
|
+
isParent: true,
|
|
1003
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1004
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1005
|
+
duplicateImage: [],
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// Add child duplicate images
|
|
1009
|
+
if ( Array.isArray( duplicate.data ) ) {
|
|
1010
|
+
parentDetail.duplicateImage = duplicate.data.map( ( child ) => ( {
|
|
1011
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${child.tempId}`,
|
|
1012
|
+
tempId: child.tempId,
|
|
1013
|
+
timeRange: child.timeRange,
|
|
1014
|
+
entryTime: child.entryTime,
|
|
1015
|
+
exitTime: child.exitTime,
|
|
1016
|
+
filePath: child.filePath,
|
|
1017
|
+
isChecked: child.isChecked !== undefined ? child.isChecked : true,
|
|
1018
|
+
} ) );
|
|
1019
|
+
|
|
1020
|
+
// Add child details to revisedDetail
|
|
1021
|
+
for ( const child of duplicate.data ) {
|
|
1022
|
+
revisedDetail.push( {
|
|
1023
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${child.tempId}`,
|
|
1024
|
+
clientId: oldSource.clientId,
|
|
1025
|
+
storeId: oldSource.storeId,
|
|
1026
|
+
tempId: child.tempId,
|
|
1027
|
+
dateString: oldSource.dateString,
|
|
1028
|
+
timeRange: child.timeRange,
|
|
1029
|
+
processType: 'footfall',
|
|
1030
|
+
revopsType: 'duplicate',
|
|
1031
|
+
entryTime: child.entryTime,
|
|
1032
|
+
exitTime: child.exitTime,
|
|
1033
|
+
filePath: child.filePath,
|
|
1034
|
+
status: oldSource.duplicateStatus || 'submitted',
|
|
1035
|
+
description: '',
|
|
1036
|
+
isChecked: child.isChecked !== undefined ? child.isChecked : false,
|
|
1037
|
+
type: 'tagging-reflect',
|
|
1038
|
+
parent: duplicate.tempId,
|
|
1039
|
+
isParent: false,
|
|
1040
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1041
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1042
|
+
duplicateImage: [],
|
|
1043
|
+
} );
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
revisedDetail.push( parentDetail );
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// Transform houseKeeping
|
|
1052
|
+
if ( Array.isArray( oldSource.houseKeeping ) ) {
|
|
1053
|
+
for ( const item of oldSource.houseKeeping ) {
|
|
1054
|
+
revisedDetail.push( {
|
|
1055
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1056
|
+
clientId: oldSource.clientId,
|
|
1057
|
+
storeId: oldSource.storeId,
|
|
1058
|
+
tempId: item.tempId,
|
|
1059
|
+
dateString: oldSource.dateString,
|
|
1060
|
+
timeRange: item.timeRange,
|
|
1061
|
+
processType: 'footfall',
|
|
1062
|
+
revopsType: 'houseKeeping',
|
|
1063
|
+
entryTime: item.entryTime,
|
|
1064
|
+
exitTime: item.exitTime,
|
|
1065
|
+
filePath: item.filePath,
|
|
1066
|
+
status: oldSource.houseKeepingStatus || 'submitted',
|
|
1067
|
+
description: '',
|
|
1068
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1069
|
+
type: 'tagging-reflect',
|
|
1070
|
+
parent: null,
|
|
1071
|
+
isParent: false,
|
|
1072
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1073
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1074
|
+
duplicateImage: [],
|
|
1075
|
+
} );
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Transform employee
|
|
1080
|
+
if ( Array.isArray( oldSource.employee ) ) {
|
|
1081
|
+
for ( const item of oldSource.employee ) {
|
|
1082
|
+
revisedDetail.push( {
|
|
1083
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1084
|
+
clientId: oldSource.clientId,
|
|
1085
|
+
storeId: oldSource.storeId,
|
|
1086
|
+
tempId: item.tempId,
|
|
1087
|
+
dateString: oldSource.dateString,
|
|
1088
|
+
timeRange: item.timeRange,
|
|
1089
|
+
processType: 'footfall',
|
|
1090
|
+
revopsType: 'employee',
|
|
1091
|
+
entryTime: item.entryTime,
|
|
1092
|
+
exitTime: item.exitTime,
|
|
1093
|
+
filePath: item.filePath,
|
|
1094
|
+
status: oldSource.employeeStatus || 'submitted',
|
|
1095
|
+
description: '',
|
|
1096
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1097
|
+
type: 'tagging-reflect',
|
|
1098
|
+
parent: null,
|
|
1099
|
+
isParent: false,
|
|
1100
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1101
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1102
|
+
duplicateImage: [],
|
|
1103
|
+
} );
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Transform junk
|
|
1108
|
+
if ( Array.isArray( oldSource.junk ) ) {
|
|
1109
|
+
for ( const item of oldSource.junk ) {
|
|
1110
|
+
revisedDetail.push( {
|
|
1111
|
+
id: `${oldSource.storeId}_${oldSource.dateString}_${item.tempId}`,
|
|
1112
|
+
clientId: oldSource.clientId,
|
|
1113
|
+
storeId: oldSource.storeId,
|
|
1114
|
+
tempId: item.tempId,
|
|
1115
|
+
dateString: oldSource.dateString,
|
|
1116
|
+
timeRange: item.timeRange,
|
|
1117
|
+
processType: 'footfall',
|
|
1118
|
+
revopsType: 'junk',
|
|
1119
|
+
entryTime: item.entryTime,
|
|
1120
|
+
exitTime: item.exitTime,
|
|
1121
|
+
filePath: item.filePath,
|
|
1122
|
+
status: 'submitted',
|
|
1123
|
+
description: '',
|
|
1124
|
+
isChecked: item.isChecked !== undefined ? item.isChecked : false,
|
|
1125
|
+
type: 'tagging-reflect',
|
|
1126
|
+
parent: null,
|
|
1127
|
+
isParent: false,
|
|
1128
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1129
|
+
updatedAt: oldSource.updatedAt || new Date(),
|
|
1130
|
+
duplicateImage: [],
|
|
1131
|
+
} );
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// Create count array
|
|
1136
|
+
const count = [
|
|
1137
|
+
{
|
|
1138
|
+
name: 'Duplicate',
|
|
1139
|
+
value: oldSource.duplicateCount || 0,
|
|
1140
|
+
key: 'duplicateCount',
|
|
1141
|
+
type: 'duplicate',
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
name: 'Employee',
|
|
1145
|
+
value: oldSource.employeeCount || 0,
|
|
1146
|
+
key: 'employeeCount',
|
|
1147
|
+
type: 'employee',
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
name: 'House keeping',
|
|
1151
|
+
value: oldSource.houseKeepingCount || 0,
|
|
1152
|
+
key: 'houseKeepingCount',
|
|
1153
|
+
type: 'houseKeeping',
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
name: 'Junk',
|
|
1157
|
+
value: oldSource.junkCount || 0,
|
|
1158
|
+
key: 'junkCount',
|
|
1159
|
+
type: 'junk',
|
|
1160
|
+
},
|
|
1161
|
+
];
|
|
1162
|
+
|
|
1163
|
+
// Create mappingInfo array
|
|
1164
|
+
const mappingInfo = [
|
|
1165
|
+
{
|
|
1166
|
+
type: 'tagging',
|
|
1167
|
+
mode: 'mobile',
|
|
1168
|
+
revicedFootfall,
|
|
1169
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1170
|
+
reviced,
|
|
1171
|
+
count,
|
|
1172
|
+
revisedDetail,
|
|
1173
|
+
status: oldSource.status === 'open' ? 'Raised' : oldSource.status || 'Raised',
|
|
1174
|
+
createdByEmail: oldSource.email || '',
|
|
1175
|
+
createdByUserName: oldSource.userName || '',
|
|
1176
|
+
createdByRole: oldSource.role || 'user',
|
|
1177
|
+
createdAt: oldSource.createdAt || new Date(),
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
type: 'review',
|
|
1181
|
+
count,
|
|
1182
|
+
revisedDetail,
|
|
1183
|
+
status: oldSource.status === 'open' ? 'Open' : oldSource.status || 'Open',
|
|
1184
|
+
dueDate: oldSource.updatedAt ? new Date( new Date( oldSource.updatedAt ).getTime() + 3 * 24 * 60 * 60 * 1000 ) : new Date( Date.now() + 3 * 24 * 60 * 60 * 1000 ),
|
|
1185
|
+
},
|
|
1186
|
+
];
|
|
1187
|
+
|
|
1188
|
+
// Create new structure
|
|
1189
|
+
const newSource = {
|
|
1190
|
+
storeId: oldSource.storeId,
|
|
1191
|
+
type: 'store',
|
|
1192
|
+
dateString: oldSource.dateString,
|
|
1193
|
+
storeName: oldSource.storeName,
|
|
1194
|
+
ticketName: oldSource.ticketName,
|
|
1195
|
+
footfallCount: oldSource.footfallCount,
|
|
1196
|
+
clientId: oldSource.clientId,
|
|
1197
|
+
ticketId: oldSource.ticketId,
|
|
1198
|
+
createdAt: oldSource.createdAt,
|
|
1199
|
+
updatedAt: oldSource.updatedAt,
|
|
1200
|
+
status: oldSource.status === 'open' ? 'Raised' : oldSource.status || 'Raised',
|
|
1201
|
+
comments: oldSource.comments || '',
|
|
1202
|
+
revicedFootfall,
|
|
1203
|
+
revicedPerc: `${revicedPerc}%`,
|
|
1204
|
+
reviced,
|
|
1205
|
+
mappingInfo,
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
// Update document in OpenSearch
|
|
1209
|
+
// await updateOpenSearchData( openSearch.footfallDirectory, documentId, { doc: newSource } );
|
|
1210
|
+
// migratedCount++;
|
|
1211
|
+
|
|
1212
|
+
logger.info( { message: 'Document migrated successfully', newSource, documentId, storeId: oldSource.storeId, dateString: oldSource.dateString } );
|
|
1213
|
+
} catch ( error ) {
|
|
1214
|
+
const errorMsg = `Error migrating document ${hit._id}: ${error.message}`;
|
|
1215
|
+
errors.push( errorMsg );
|
|
1216
|
+
logger.error( { error: error, documentId: hit._id, function: 'vmsDataMigration' } );
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return res.sendSuccess( {
|
|
1221
|
+
message: `Migration completed. ${migratedCount} document(s) migrated.`,
|
|
1222
|
+
migrated: migratedCount,
|
|
1223
|
+
total: hits.length,
|
|
1224
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
1225
|
+
} );
|
|
1226
|
+
} catch ( error ) {
|
|
1227
|
+
logger.error( { error: error, message: req.query, function: 'vmsDataMigration' } );
|
|
1228
|
+
const err = error.message || 'Internal Server Error';
|
|
1229
|
+
return res.sendError( err, 500 );
|
|
1230
|
+
}
|
|
1231
|
+
}
|