tango-app-api-analysis-traffic 3.8.14 → 3.8.15-alpha.0
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
|
@@ -702,12 +702,16 @@ export async function expireReviewStatus( req, res ) {
|
|
|
702
702
|
export async function expireApproveStatus( req, res ) {
|
|
703
703
|
try {
|
|
704
704
|
const {
|
|
705
|
-
thresholdDate
|
|
705
|
+
thresholdDate,
|
|
706
706
|
batchSize = 500,
|
|
707
707
|
storeId,
|
|
708
708
|
dateString,
|
|
709
709
|
} = req.body;
|
|
710
710
|
const cutoffDate = new Date( thresholdDate );
|
|
711
|
+
// Convert cutoffDate to "2026-01-12T23:59:59.000Z"
|
|
712
|
+
|
|
713
|
+
cutoffDate.setUTCHours( 23, 59, 59, 0 );
|
|
714
|
+
|
|
711
715
|
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
712
716
|
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
713
717
|
}
|
|
@@ -761,7 +765,6 @@ export async function expireApproveStatus( req, res ) {
|
|
|
761
765
|
let updatedMapping = mappingInfo.map( ( item ) => {
|
|
762
766
|
if ( item?.type === 'approve' && item?.status !== 'Closed' && item?.dueDate ) {
|
|
763
767
|
const due = new Date( item.dueDate );
|
|
764
|
-
logger.info( { item, due, msg: '..........1', cutoffDate } );
|
|
765
768
|
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
766
769
|
changed = true;
|
|
767
770
|
|
|
@@ -771,24 +774,19 @@ export async function expireApproveStatus( req, res ) {
|
|
|
771
774
|
|
|
772
775
|
return item;
|
|
773
776
|
} );
|
|
774
|
-
logger.info( { updatedMapping, msh: '.......12' } );
|
|
775
777
|
if ( changed ) {
|
|
776
|
-
logger.info( { changed, msg: '.......2' } );
|
|
777
778
|
updatedMapping = updatedMapping.map( ( item ) => {
|
|
778
|
-
logger.info( { item, msg: '.......3' } );
|
|
779
779
|
if ( item?.type === 'tagging' ) {
|
|
780
|
-
logger.info( { item: item?.type, msg: '.......4' } );
|
|
781
780
|
return { ...item, status: 'Expired' };
|
|
782
781
|
}
|
|
783
782
|
return item;
|
|
784
783
|
} );
|
|
785
784
|
}
|
|
786
785
|
|
|
787
|
-
logger.info( { updatedMapping, msh: '.......13' } );
|
|
788
786
|
if ( changed ) {
|
|
789
787
|
const doc = {
|
|
790
788
|
mappingInfo: updatedMapping,
|
|
791
|
-
status: 'Expired',
|
|
789
|
+
status: 'Approver-Expired',
|
|
792
790
|
};
|
|
793
791
|
logger.info( { updatedMapping } );
|
|
794
792
|
bulkBody.push(
|
|
@@ -814,7 +812,130 @@ export async function expireApproveStatus( req, res ) {
|
|
|
814
812
|
|
|
815
813
|
return res.sendSuccess( { message: 'Expired approve status updated', updated: totalUpdated } );
|
|
816
814
|
} catch ( error ) {
|
|
817
|
-
logger.error( { error: error, message: req.body, function: '
|
|
815
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
816
|
+
return res.sendError( { error: error }, 500 );
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
export async function expireTangoReviewStatus( req, res ) {
|
|
822
|
+
try {
|
|
823
|
+
const {
|
|
824
|
+
thresholdDate,
|
|
825
|
+
batchSize = 500,
|
|
826
|
+
storeId,
|
|
827
|
+
dateString,
|
|
828
|
+
} = req.body;
|
|
829
|
+
const cutoffDate = new Date( thresholdDate );
|
|
830
|
+
// Convert cutoffDate to "2026-01-12T23:59:59.000Z"
|
|
831
|
+
|
|
832
|
+
cutoffDate.setUTCHours( 23, 59, 59, 0 );
|
|
833
|
+
|
|
834
|
+
if ( Number.isNaN( cutoffDate.getTime() ) ) {
|
|
835
|
+
return res.sendError( 'Invalid thresholdDate', 400 );
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
840
|
+
const query = {
|
|
841
|
+
size: batchSize,
|
|
842
|
+
query: {
|
|
843
|
+
bool: {
|
|
844
|
+
must: [
|
|
845
|
+
{ term: { 'ticketName.keyword': 'footfall-directory' } },
|
|
846
|
+
{ term: { 'type.keyword': 'store' } },
|
|
847
|
+
],
|
|
848
|
+
must_not: [
|
|
849
|
+
{ terms: { 'status.keyword': [ 'Closed' ] } },
|
|
850
|
+
],
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
if ( storeId ) {
|
|
857
|
+
query.query.bool.must.push( { term: { 'storeId.keyword': storeId } } );
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
if ( dateString ) {
|
|
862
|
+
query.query.bool.must.push( {
|
|
863
|
+
terms: {
|
|
864
|
+
dateString: Array.isArray( dateString ) ? dateString : `${dateString}`.split( ',' ),
|
|
865
|
+
},
|
|
866
|
+
} );
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
let totalUpdated = 0;
|
|
871
|
+
let scrollId = null;
|
|
872
|
+
|
|
873
|
+
let firstResponse = await searchOpenSearchData( openSearch.footfallDirectory, query );
|
|
874
|
+
let hitsBatch = firstResponse?.body?.hits?.hits || [];
|
|
875
|
+
scrollId = firstResponse?.body?._scroll_id;
|
|
876
|
+
|
|
877
|
+
while ( hitsBatch.length > 0 ) {
|
|
878
|
+
const bulkBody = [];
|
|
879
|
+
|
|
880
|
+
for ( const hit of hitsBatch ) {
|
|
881
|
+
const src = hit._source || {};
|
|
882
|
+
const mappingInfo = Array.isArray( src.mappingInfo ) ? src.mappingInfo : [];
|
|
883
|
+
let changed = false;
|
|
884
|
+
let updatedMapping = mappingInfo.map( ( item ) => {
|
|
885
|
+
if ( item?.type === 'tangoreview' && item?.status !== 'Closed' && item?.dueDate ) {
|
|
886
|
+
const due = new Date( item.dueDate );
|
|
887
|
+
if ( !Number.isNaN( due.getTime() ) && due < cutoffDate ) {
|
|
888
|
+
changed = true;
|
|
889
|
+
|
|
890
|
+
return { ...item, status: 'Expired' };
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return item;
|
|
895
|
+
} );
|
|
896
|
+
if ( changed ) {
|
|
897
|
+
const getConfig = await clientService.findOne( src.clientId, { 'footfallDirectoryConfigs.tangoApproved': 1 } );
|
|
898
|
+
if ( getConfig?.footfallDirectoryConfigs?.tangoApproved && getConfig?.footfallDirectoryConfigs?.tangoApproved === true ) {
|
|
899
|
+
;
|
|
900
|
+
updatedMapping = updatedMapping.map( ( item ) => {
|
|
901
|
+
if ( item?.type === 'tagging' ) {
|
|
902
|
+
return { ...item, status: 'Expired' };
|
|
903
|
+
}
|
|
904
|
+
return item;
|
|
905
|
+
} );
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if ( changed ) {
|
|
910
|
+
const doc = {
|
|
911
|
+
mappingInfo: updatedMapping,
|
|
912
|
+
status: 'Tango-Expired',
|
|
913
|
+
};
|
|
914
|
+
logger.info( { updatedMapping } );
|
|
915
|
+
bulkBody.push(
|
|
916
|
+
{ update: { _index: openSearch.footfallDirectory, _id: hit._id } },
|
|
917
|
+
{ doc: doc, doc_as_upsert: true },
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if ( bulkBody.length > 0 ) {
|
|
923
|
+
const bulkRes = await bulkUpdate( bulkBody );
|
|
924
|
+
if ( bulkRes?.errors ) {
|
|
925
|
+
logger.error( { message: 'Bulk expire errors', items: bulkRes.items } );
|
|
926
|
+
}
|
|
927
|
+
totalUpdated += bulkBody.length / 2;
|
|
928
|
+
}
|
|
929
|
+
logger.info( { totalUpdated, msg: '........9' } );
|
|
930
|
+
if ( !scrollId ) break;
|
|
931
|
+
const nextRes = await scrollResponse( scrollId );
|
|
932
|
+
hitsBatch = nextRes?.body?.hits?.hits || [];
|
|
933
|
+
scrollId = nextRes?.body?._scroll_id;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return res.sendSuccess( { message: 'Expired approve status updated', updated: totalUpdated } );
|
|
937
|
+
} catch ( error ) {
|
|
938
|
+
logger.error( { error: error, message: req.body, function: 'expireReviewStatus' } );
|
|
818
939
|
return res.sendError( { error: error }, 500 );
|
|
819
940
|
}
|
|
820
941
|
}
|
|
@@ -1294,6 +1415,7 @@ export async function tagTempId( req, res ) {
|
|
|
1294
1415
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
1295
1416
|
|
|
1296
1417
|
const upsertRecord = {
|
|
1418
|
+
mode: inputData.mode || '',
|
|
1297
1419
|
clientId: inputData.storeId.split( '-' )[0],
|
|
1298
1420
|
storeId: inputData.storeId,
|
|
1299
1421
|
tempId: inputData.tempId,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus } from '../controllers/revop.controller.js';
|
|
3
|
+
import { storeProcessedData, getconfig, revoptagging, getrevoptagging, revoptaggingcount, footFallImages, tagTempId, getCategorizedImages, vmsDataMigration, migrateRevopIndex, expireReviewStatus, expireApproveStatus } from '../controllers/revop.controller.js';
|
|
4
4
|
import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
|
|
5
5
|
import { footfallImagesValid, getCategorizedImagesValid, storeProcessedDataValid, tagTempIdValid, vmsDataMigrationValid } from '../dtos/revop.dtos.js';
|
|
6
|
-
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig } from '../validations/revop.validation.js';
|
|
6
|
+
import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig, validateDateAndFilePath } from '../validations/revop.validation.js';
|
|
7
|
+
|
|
7
8
|
|
|
8
9
|
export const revopRouter = express.Router();
|
|
9
10
|
|
|
@@ -13,12 +14,13 @@ revopRouter
|
|
|
13
14
|
.post( '/getrevoptagging', isAllowedSessionHandler, getrevoptagging )
|
|
14
15
|
.post( '/migrate-revop', migrateRevopIndex )
|
|
15
16
|
.post( '/expire-review-status', expireReviewStatus )
|
|
17
|
+
.post( '/expire-approve-status', expireApproveStatus )
|
|
16
18
|
.post( '/revoptaggingcount', isAllowedSessionHandler, revoptaggingcount )
|
|
17
19
|
|
|
18
|
-
// new
|
|
20
|
+
// new enhancement (for footfall directory)
|
|
19
21
|
.get( '/store-processed-data', isAllowedSessionHandler, validate( storeProcessedDataValid ), storeProcessedData )
|
|
20
22
|
.get( '/footfall-images', isAllowedSessionHandler, validate( footfallImagesValid ), getTaggingConfig, footFallImages )
|
|
21
|
-
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, tagTempId )
|
|
23
|
+
.post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, validateDateAndFilePath, tagTempId )
|
|
22
24
|
.post( '/get-categorized-images', isAllowedSessionHandler, validate( getCategorizedImagesValid ), getCategorizedImages )
|
|
23
25
|
.post( '/vms-data-migration', validate( vmsDataMigrationValid ), vmsDataMigration );
|
|
24
26
|
|
|
@@ -366,3 +366,38 @@ export async function deleteTaggedDuplicate( req, res, next ) {
|
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
|
|
370
|
+
export async function validateDateAndFilePath( req, res, next ) {
|
|
371
|
+
try {
|
|
372
|
+
const inputData = req.body;
|
|
373
|
+
const getDateString = inputData?.dateString;
|
|
374
|
+
const dateString = getDateString ?
|
|
375
|
+
getDateString.split( '-' ).reverse().join( '-' ) :
|
|
376
|
+
'';
|
|
377
|
+
const filePath = inputData?.filePath;
|
|
378
|
+
|
|
379
|
+
if ( !dateString ) {
|
|
380
|
+
return res.sendError( 'Missing payload field: dateString', 400 );
|
|
381
|
+
}
|
|
382
|
+
if ( !filePath ) {
|
|
383
|
+
return res.sendError( 'Missing payload field: filePath', 400 );
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Based on requirement: match payload dateString with filePath.split[1]
|
|
387
|
+
// Example: 11-1001/06-05-2026/5_customer_entry_frame.jpeg
|
|
388
|
+
// split('/') => [ '11-1001', '06-05-2026', '5_customer_entry_frame.jpeg' ] => index 1 is '06-05-2026'
|
|
389
|
+
const parts = String( filePath ).split( '/' );
|
|
390
|
+
const pathDateString = parts?.[1];
|
|
391
|
+
|
|
392
|
+
if ( dateString !== pathDateString ) {
|
|
393
|
+
return res.sendError( `Invalid payload: Image Date Mismatch `, 400 );
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
next();
|
|
397
|
+
} catch ( error ) {
|
|
398
|
+
logger.error( { error: error, message: req.body, function: 'traffic-revop-validateDateAndFilePath' } );
|
|
399
|
+
return res.sendError( error.message || 'Error validating date and filePath', 400 );
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|