tango-app-api-analysis-traffic 3.8.14 → 3.8.15

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",
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
  }
@@ -89,6 +89,16 @@ export const validateOverallCharParams = {
89
89
  body: validateOverallCharSchema,
90
90
  };
91
91
 
92
+ // Same as validateOverallCharSchema, plus an optional hourFormat key used only by overallHourlyChart_v3
93
+ export const validateOverallHourlyCharSchema = validateOverallCharSchema.keys( {
94
+ hourBucket: joi.number().optional(),
95
+ } );
96
+
97
+ export const validateOverallHourlyCharParams = {
98
+ body: validateOverallHourlyCharSchema,
99
+ ...validateOverallCharSchema,
100
+ };
101
+
92
102
  export const validateSingleStoreChartSchema = joi.object( {
93
103
  ...baseSchema,
94
104
  limit: joi.number().required(),
@@ -1,6 +1,6 @@
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
6
  import { deleteTaggedDuplicate, getTaggingConfig, mappingConfig } from '../validations/revop.validation.js';
@@ -13,9 +13,10 @@ revopRouter
13
13
  .post( '/getrevoptagging', isAllowedSessionHandler, getrevoptagging )
14
14
  .post( '/migrate-revop', migrateRevopIndex )
15
15
  .post( '/expire-review-status', expireReviewStatus )
16
+ .post( '/expire-approve-status', expireApproveStatus )
16
17
  .post( '/revoptaggingcount', isAllowedSessionHandler, revoptaggingcount )
17
18
 
18
- // new enhnacemnet (for footfall directory)
19
+ // new enhancement (for footfall directory)
19
20
  .get( '/store-processed-data', isAllowedSessionHandler, validate( storeProcessedDataValid ), storeProcessedData )
20
21
  .get( '/footfall-images', isAllowedSessionHandler, validate( footfallImagesValid ), getTaggingConfig, footFallImages )
21
22
  .post( '/tag-tempId', isAllowedSessionHandler, validate( tagTempIdValid ), mappingConfig, deleteTaggedDuplicate, tagTempId )
@@ -159,7 +159,7 @@ analysisTrafficRouter
159
159
  .post( '/densityDwell_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateDensityDwellParams ), densityDwellV3 )
160
160
  .post( '/trafficDensityDwell_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateDensityDwellParams ), trafficDensityDwellV3 )
161
161
  .post( '/overallCards_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateOverallCharParams ), overallCardsV3 )
162
- .post( '/overallHourlyChart_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateOverallCharParams ), overallHourlyChartV3 )
162
+ .post( '/overallHourlyChart_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateOverallHourlyCharParams ), overallHourlyChartV3 )
163
163
  .post( '/overallChart_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateOverallCharParams ), overallChartV3 )
164
164
  .post( '/singleStoreChart_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateSingleStoreChartParams ), singleStoreChartV3 )
165
165
  .post( '/demographicChart_v3', isAllowedSessionHandler, isAllowedClient, validate( validationDtos.validateDemographicChartParams ), demographicChartV3 )