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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-analysis-traffic",
3
- "version": "3.8.14",
3
+ "version": "3.8.15-alpha.0",
4
4
  "description": "Traffic Analysis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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 = '2026-01-21',
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: 'expireApproveStatus' } );
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 enhnacemnet (for footfall directory)
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
+