tango-app-api-infra 3.9.5-vms.84 → 3.9.5-vms.85

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-infra",
3
- "version": "3.9.5-vms.84",
3
+ "version": "3.9.5-vms.85",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -902,9 +902,13 @@ export async function ticketSummary( req, res ) {
902
902
  const userInfo = req.user;
903
903
  const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
904
904
  const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
905
-
905
+ const getConfig = await findOneClient( { clientId: inputData?.clientId }, { footfallDirectoryConfigs: 1 } );
906
+ if ( !getConfig || getConfig ==null ) {
907
+ return res.sendError( 'this client not configured against footfall directory', 400 );
908
+ }
909
+ inputData.clientId = inputData?.clientId?.split( ',' );
906
910
  // const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
907
- if ( req?.user?.userType === 'tango' ) {
911
+ if ( req?.user?.userType !== 'tango' ) {
908
912
  switch ( inputData?.tangoType ) {
909
913
  case 'store':
910
914
  const storeQuery = {
@@ -921,6 +925,24 @@ export async function ticketSummary( req, res ) {
921
925
  },
922
926
  },
923
927
  },
928
+
929
+ {
930
+ terms: {
931
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
932
+ inputData.clientId :
933
+ [ inputData.clientId ],
934
+ },
935
+
936
+ },
937
+
938
+ {
939
+ range: {
940
+ reviced: {
941
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
942
+ }
943
+ ,
944
+ },
945
+ },
924
946
  {
925
947
  nested: {
926
948
  path: 'mappingInfo',
@@ -983,20 +1005,7 @@ export async function ticketSummary( req, res ) {
983
1005
  size: 0,
984
1006
  aggs: {
985
1007
  avg_value: {
986
- avg: {
987
- script: {
988
- lang: 'painless',
989
- source: `
990
- if (doc['revicedPerc.keyword'].size() == 0) return null;
991
- String v = doc['revicedPerc.keyword'].value.replace('%','');
992
- try {
993
- return Double.parseDouble(v);
994
- } catch (Exception e) {
995
- return null;
996
- }
997
- `,
998
- },
999
- },
1008
+ avg: 'reviced',
1000
1009
  },
1001
1010
  },
1002
1011
  };
@@ -1029,11 +1038,10 @@ export async function ticketSummary( req, res ) {
1029
1038
  // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
1030
1039
  let openInfraIssues = 0;
1031
1040
 
1032
- let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
1041
+ let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open - Accuracy Issue' );
1033
1042
  oiQ.size = 0;
1034
1043
  const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
1035
1044
  openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
1036
-
1037
1045
  // inprogress: mappingInfo.status: 'in-Progress'
1038
1046
  let inprogress = 0;
1039
1047
 
@@ -1063,14 +1071,9 @@ export async function ticketSummary( req, res ) {
1063
1071
  // For this, add a filter on revicedPerc >= 85
1064
1072
  let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1065
1073
  {
1066
- script: {
1067
- script: {
1068
- lang: 'painless',
1069
- source: `
1070
- doc['revicedPerc.keyword'].size()!=0 &&
1071
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1072
- `,
1073
- params: { num: 85 },
1074
+ range: {
1075
+ reviced: {
1076
+ gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1074
1077
  },
1075
1078
  },
1076
1079
  },
@@ -1085,14 +1088,9 @@ export async function ticketSummary( req, res ) {
1085
1088
 
1086
1089
  let belowQ = buildAggStoreQuery( baseStoreQuery, [
1087
1090
  {
1088
- script: {
1089
- script: {
1090
- lang: 'painless',
1091
- source: `
1092
- doc['revicedPerc.keyword'].size()!=0 &&
1093
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1094
- `,
1095
- params: { num: 85 },
1091
+ range: {
1092
+ reviced: {
1093
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1096
1094
  },
1097
1095
  },
1098
1096
  },
@@ -1128,23 +1126,48 @@ export async function ticketSummary( req, res ) {
1128
1126
  },
1129
1127
  },
1130
1128
  },
1129
+ {
1130
+ terms: {
1131
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1132
+ inputData.clientId :
1133
+ [ inputData.clientId ],
1134
+ },
1135
+
1136
+ },
1137
+
1138
+ ],
1139
+ },
1140
+ },
1141
+ };
1131
1142
 
1143
+ const internalOpen = {
1144
+ size: 0,
1145
+ query: {
1146
+ bool: {
1147
+ must: [
1132
1148
  {
1133
- nested: {
1134
- path: 'mappingInfo',
1135
- query: {
1136
- bool: {
1137
- must: [
1138
- {
1139
- terms: {
1140
- 'mappingInfo.type': [ 'tagging', 'review', 'approve', 'tangoreview' ],
1141
- },
1142
- },
1143
- ],
1144
- },
1149
+ 'range': {
1150
+ 'dateString': {
1151
+ 'gte': inputData?.fromDate,
1152
+ 'lte': inputData?.toDate,
1153
+ 'format': 'yyyy-MM-dd',
1145
1154
  },
1146
1155
  },
1147
1156
  },
1157
+ {
1158
+ terms: {
1159
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1160
+ inputData.clientId :
1161
+ [ inputData.clientId ],
1162
+ },
1163
+
1164
+ },
1165
+ {
1166
+ terms: {
1167
+ 'status.keyword': [ 'Open', 'Raised' ],
1168
+ },
1169
+
1170
+ },
1148
1171
 
1149
1172
  ],
1150
1173
  },
@@ -1154,20 +1177,32 @@ export async function ticketSummary( req, res ) {
1154
1177
  // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1155
1178
  function buildInternalQueryWithStatus( baseQuery, statusValue ) {
1156
1179
  let q = JSON.parse( JSON.stringify( baseQuery ) );
1157
- // Remove any previous mappingInfo.status term
1180
+ // Check if nested query exists, if not create it
1158
1181
  let nested = q.query.bool.must.find( ( m ) => m.nested );
1159
- if ( nested ) {
1160
- // filter out all mappingInfo.status
1161
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1162
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1163
- } );
1164
- // add desired status
1165
- nested.nested.query.bool.must.push( {
1166
- term: {
1167
- 'mappingInfo.status': statusValue,
1182
+ if ( !nested ) {
1183
+ // Create nested query structure
1184
+ nested = {
1185
+ nested: {
1186
+ path: 'mappingInfo',
1187
+ query: {
1188
+ bool: {
1189
+ must: [],
1190
+ },
1191
+ },
1168
1192
  },
1169
- } );
1193
+ };
1194
+ q.query.bool.must.push( nested );
1170
1195
  }
1196
+ // filter out all mappingInfo.status
1197
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1198
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1199
+ } );
1200
+ // add desired status
1201
+ nested.nested.query.bool.must.push( {
1202
+ term: {
1203
+ 'mappingInfo.status': statusValue,
1204
+ },
1205
+ } );
1171
1206
  return q;
1172
1207
  }
1173
1208
 
@@ -1183,7 +1218,7 @@ export async function ticketSummary( req, res ) {
1183
1218
  nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1184
1219
 
1185
1220
  // add new filters
1186
- nested.nested.query.bool.must.push( ...filters );
1221
+ q.query.bool.must.push( ...filters );
1187
1222
  }
1188
1223
 
1189
1224
  return {
@@ -1192,18 +1227,7 @@ export async function ticketSummary( req, res ) {
1192
1227
  aggs: {
1193
1228
  avg_value: {
1194
1229
  avg: {
1195
- script: {
1196
- lang: 'painless',
1197
- source: `
1198
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1199
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1200
- try {
1201
- return Double.parseDouble(v);
1202
- } catch (Exception e) {
1203
- return null;
1204
- }
1205
- `,
1206
- },
1230
+ field: 'reviced',
1207
1231
  },
1208
1232
  },
1209
1233
  },
@@ -1224,13 +1248,14 @@ export async function ticketSummary( req, res ) {
1224
1248
  allInternalQuery.size = 0;
1225
1249
  const totalInternalResp = await getOpenSearchData( openSearch.footfallDirectory, allInternalQuery );
1226
1250
  totalInternalTickets = totalInternalResp?.body?.hits?.total?.value || 0;
1251
+ logger.info( { totalInternalResp } );
1227
1252
 
1228
1253
  // openTickets: mappingInfo.status: 'Open'
1229
1254
  let openInternalTickets = 0;
1230
1255
 
1231
- let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
1232
- otQInternal.size = 0;
1233
- const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, otQInternal );
1256
+ // let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
1257
+ // otQInternal.size = 0;
1258
+ const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, internalOpen );
1234
1259
  openInternalTickets = openInternalResp?.body?.hits?.total?.value || 0;
1235
1260
  // logger.info( { msd: '..............2', openResp } );
1236
1261
 
@@ -1238,11 +1263,12 @@ export async function ticketSummary( req, res ) {
1238
1263
  // openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
1239
1264
  let openInternalInfraIssues = 0;
1240
1265
 
1241
- let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open Accuracy Issue' );
1266
+ let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open - Accuracy Issue' );
1242
1267
  oiQinternal.size = 0;
1243
1268
  const infraInternalResp = await getOpenSearchData( openSearch.footfallDirectory, oiQinternal );
1244
1269
  openInternalInfraIssues = infraInternalResp?.body?.hits?.total?.value || 0;
1245
1270
 
1271
+
1246
1272
  // inprogress: mappingInfo.status: 'in-Progress'
1247
1273
  let inprogressIntrenal = 0;
1248
1274
 
@@ -1272,14 +1298,9 @@ export async function ticketSummary( req, res ) {
1272
1298
  // For this, add a filter on revicedPerc >= 85
1273
1299
  let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
1274
1300
  {
1275
- script: {
1276
- script: {
1277
- lang: 'painless',
1278
- source: `
1279
- doc['revicedPerc.keyword'].size()!=0 &&
1280
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1281
- `,
1282
- params: { num: 85 },
1301
+ range: {
1302
+ reviced: {
1303
+ gte: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1283
1304
  },
1284
1305
  },
1285
1306
  },
@@ -1294,14 +1315,9 @@ export async function ticketSummary( req, res ) {
1294
1315
 
1295
1316
  let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
1296
1317
  {
1297
- script: {
1298
- script: {
1299
- lang: 'painless',
1300
- source: `
1301
- doc['revicedPerc.keyword'].size()!=0 &&
1302
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1303
- `,
1304
- params: { num: 85 },
1318
+ range: {
1319
+ reviced: {
1320
+ lt: parseInt( getConfig?.footfallDirectoryConfigs?.tangoReview ),
1305
1321
  },
1306
1322
  },
1307
1323
  },
@@ -1324,8 +1340,10 @@ export async function ticketSummary( req, res ) {
1324
1340
  break;
1325
1341
  default: '';
1326
1342
  }
1327
- } else if ( req?.user?.userType === 'client' ) {
1343
+ } else if ( req?.user?.userType === 'tango' ) {
1344
+ logger.info( { msg: '.........1', ticketsFeature, ticketsApproveFeature } );
1328
1345
  if ( ticketsFeature && !ticketsApproveFeature ) {
1346
+ logger.info( '.........3' );
1329
1347
  const storeQuery = {
1330
1348
  size: 0,
1331
1349
  query: {
@@ -1340,6 +1358,14 @@ export async function ticketSummary( req, res ) {
1340
1358
  },
1341
1359
  },
1342
1360
  },
1361
+ {
1362
+ terms: {
1363
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1364
+ inputData.clientId :
1365
+ [ inputData.clientId ],
1366
+ },
1367
+
1368
+ },
1343
1369
  {
1344
1370
  nested: {
1345
1371
  path: 'mappingInfo',
@@ -1402,20 +1428,7 @@ export async function ticketSummary( req, res ) {
1402
1428
  size: 0,
1403
1429
  aggs: {
1404
1430
  avg_value: {
1405
- avg: {
1406
- script: {
1407
- lang: 'painless',
1408
- source: `
1409
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1410
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1411
- try {
1412
- return Double.parseDouble(v);
1413
- } catch (Exception e) {
1414
- return null;
1415
- }
1416
- `,
1417
- },
1418
- },
1431
+ avg: 'mappingInfo.reviced',
1419
1432
  },
1420
1433
  },
1421
1434
  };
@@ -1490,48 +1503,44 @@ try {
1490
1503
  const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1491
1504
  expiredTickets = eResp?.body?.hits?.total?.value || 0;
1492
1505
 
1493
- let ticketAccuracyAbove = 0;
1494
- // For this, add a filter on revicedPerc >= 85
1495
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1496
- {
1497
- script: {
1506
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1507
+
1508
+ // Build aggregation query for ticket percentage
1509
+ let ticketPercentageAvg = 0;
1510
+
1511
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1512
+ avgTicketPercentageQuery.size = 0;
1513
+ avgTicketPercentageQuery.aggs = {
1514
+ avg_ticket_percentage: {
1515
+ avg: {
1498
1516
  script: {
1499
- lang: 'painless',
1500
1517
  source: `
1501
- doc['revicedPerc.keyword'].size()!=0 &&
1502
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1518
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1519
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1520
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1521
+ } else {
1522
+ return null;
1523
+ }
1503
1524
  `,
1504
- params: { num: 85 },
1525
+ lang: 'painless',
1505
1526
  },
1506
1527
  },
1507
1528
  },
1529
+ };
1508
1530
 
1531
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1509
1532
 
1510
- ] );
1511
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1512
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1533
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1513
1534
 
1535
+ logger.info( { avgTicketPercentageResp } );
1514
1536
  // ticketAccuracyBelow: avg of revicedPerc < 85%
1515
- let ticketAccuracyBelow = 0;
1516
-
1517
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1518
- {
1519
- script: {
1520
- script: {
1521
- lang: 'painless',
1522
- source: `
1523
- doc['revicedPerc.keyword'].size()!=0 &&
1524
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1525
- `,
1526
- params: { num: 85 },
1527
- },
1528
- },
1529
- },
1530
-
1531
- ] );
1532
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1533
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1537
+ let ticketAccuracy = 0;
1534
1538
 
1539
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1540
+ logger.info( { belowQ } );
1541
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1542
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1543
+ logger.info( { accuracyResp } );
1535
1544
  // Final result object
1536
1545
  result = {
1537
1546
  totalTickets,
@@ -1540,465 +1549,435 @@ try {
1540
1549
  closedTickets,
1541
1550
  dueToday: dueToday,
1542
1551
  Expired: expiredTickets,
1543
- avgTicket: ticketAccuracyAbove+'%',
1544
- avgAccuracy: ticketAccuracyBelow+'%',
1552
+ avgTicket: ticketPercentageAvg+'%',
1553
+ avgAccuracy: ticketAccuracy+'%',
1545
1554
  };
1546
- }
1547
- } else if ( ticketsFeature && ticketsApproveFeature ) {
1548
- if ( inputData?.permissionType === 'review' ) {
1549
- const storeQuery = {
1550
- size: 0,
1551
- query: {
1552
- bool: {
1553
- must: [
1554
- {
1555
- 'range': {
1556
- 'dateString': {
1557
- 'gte': inputData?.fromDate,
1558
- 'lte': inputData?.toDate,
1559
- 'format': 'yyyy-MM-dd',
1555
+ } else if ( ticketsFeature && ticketsApproveFeature ) {
1556
+ logger.info( '.........2' );
1557
+ if ( inputData?.permissionType === 'review' ) {
1558
+ const storeQuery = {
1559
+ size: 0,
1560
+ query: {
1561
+ bool: {
1562
+ must: [
1563
+ {
1564
+ 'range': {
1565
+ 'dateString': {
1566
+ 'gte': inputData?.fromDate,
1567
+ 'lte': inputData?.toDate,
1568
+ 'format': 'yyyy-MM-dd',
1569
+ },
1560
1570
  },
1561
1571
  },
1562
- },
1563
- {
1564
- nested: {
1565
- path: 'mappingInfo',
1566
- query: {
1567
- bool: {
1568
- must: [
1569
- {
1570
- term: {
1571
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1572
+ {
1573
+ terms: {
1574
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1575
+ inputData.clientId :
1576
+ [ inputData.clientId ],
1577
+ },
1578
+
1579
+ },
1580
+ {
1581
+ nested: {
1582
+ path: 'mappingInfo',
1583
+ query: {
1584
+ bool: {
1585
+ must: [
1586
+ {
1587
+ term: {
1588
+ 'mappingInfo.type': 'review',
1589
+ },
1572
1590
  },
1573
- },
1574
- ],
1591
+ ],
1592
+ },
1575
1593
  },
1576
1594
  },
1577
1595
  },
1578
- },
1579
1596
 
1580
- ],
1597
+ ],
1598
+ },
1581
1599
  },
1582
- },
1583
- };
1600
+ };
1584
1601
 
1585
- // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1586
- function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1587
- let q = JSON.parse( JSON.stringify( baseQuery ) );
1588
- // Remove any previous mappingInfo.status term
1589
- let nested = q.query.bool.must.find( ( m ) => m.nested );
1590
- if ( nested ) {
1591
- // filter out all mappingInfo.status
1592
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1593
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1594
- } );
1595
- // add desired status
1596
- nested.nested.query.bool.must.push( {
1597
- term: {
1598
- 'mappingInfo.status': statusValue,
1599
- },
1600
- } );
1602
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1603
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1604
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1605
+ // Remove any previous mappingInfo.status term
1606
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1607
+ if ( nested ) {
1608
+ // filter out all mappingInfo.status
1609
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1610
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1611
+ } );
1612
+ // add desired status
1613
+ nested.nested.query.bool.must.push( {
1614
+ term: {
1615
+ 'mappingInfo.status': statusValue,
1616
+ },
1617
+ } );
1618
+ }
1619
+ return q;
1601
1620
  }
1602
- return q;
1603
- }
1604
1621
 
1605
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1606
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1622
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1623
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1607
1624
 
1608
- // locate nested section
1609
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1625
+ // locate nested section
1626
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1610
1627
 
1611
- if ( nested ) {
1612
- // remove old status filters
1613
- nested.nested.query.bool.must =
1614
- nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1628
+ if ( nested ) {
1629
+ // remove old status filters
1630
+ nested.nested.query.bool.must =
1631
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1615
1632
 
1616
- // add new filters
1617
- nested.nested.query.bool.must.push( ...filters );
1618
- }
1633
+ // add new filters
1634
+ nested.nested.query.bool.must.push( ...filters );
1635
+ }
1619
1636
 
1620
- return {
1621
- ...q,
1622
- size: 0,
1623
- aggs: {
1624
- avg_value: {
1625
- avg: {
1626
- script: {
1627
- lang: 'painless',
1628
- source: `
1629
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1630
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1631
- try {
1632
- return Double.parseDouble(v);
1633
- } catch (Exception e) {
1634
- return null;
1635
- }
1636
- `,
1637
- },
1637
+ return {
1638
+ ...q,
1639
+ size: 0,
1640
+ aggs: {
1641
+ avg_value: {
1642
+ avg: 'mappingInfo.reviced',
1638
1643
  },
1639
1644
  },
1640
- },
1645
+ };
1641
1646
  };
1642
- };
1643
1647
 
1644
1648
 
1645
- // Get OpenSearch connection
1646
-
1647
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1648
-
1649
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1650
- let totalTickets = 0;
1649
+ // Get OpenSearch connection
1651
1650
 
1652
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1651
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1653
1652
 
1654
- allQuery.size = 0;
1655
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1656
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1653
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1654
+ let totalTickets = 0;
1657
1655
 
1658
- // openTickets: mappingInfo.status: 'Open'
1659
- let openTickets = 0;
1656
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1660
1657
 
1661
- let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1662
- otQ.size = 0;
1663
- const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1664
- openTickets = openResp?.body?.hits?.total?.value || 0;
1665
- // logger.info( { msd: '..............2', openResp } );
1658
+ allQuery.size = 0;
1659
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1660
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1666
1661
 
1662
+ // openTickets: mappingInfo.status: 'Open'
1663
+ let openTickets = 0;
1667
1664
 
1668
- // inprogress: mappingInfo.status: 'in-Progress'
1669
- let inprogress = 0;
1665
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1666
+ otQ.size = 0;
1667
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1668
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1669
+ // logger.info( { msd: '..............2', openResp } );
1670
1670
 
1671
- let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1672
- ipQ.size = 0;
1673
- const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1674
- inprogress = ipResp?.body?.hits?.total?.value || 0;
1675
1671
 
1676
- // closedTickets: mappingInfo.status: 'closed'
1677
- let closedTickets = 0;
1672
+ // inprogress: mappingInfo.status: 'in-Progress'
1673
+ let inprogress = 0;
1678
1674
 
1679
- let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1680
- clQ.size = 0;
1681
- const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1682
- closedTickets = clResp?.body?.hits?.total?.value || 0;
1683
- // Average revisedPerc (for all tangoreview)
1675
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1676
+ ipQ.size = 0;
1677
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1678
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1684
1679
 
1685
- // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1686
- let dueToday = 0;
1687
- let todayDateString = new Date().toISOString().slice( 0, 10 );
1680
+ // closedTickets: mappingInfo.status: 'closed'
1681
+ let closedTickets = 0;
1688
1682
 
1689
- // Build a query for tickets with mappingInfo.dueDate == todayDateString
1690
- let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1691
- // Locate nested mappingInfo query
1692
- let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1693
- if ( nestedDue ) {
1694
- // Remove any previous mappingInfo.dueDate term
1695
- nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1696
- ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1697
- );
1698
- // Add new dueDate filter
1699
- nestedDue.nested.query.bool.must.push( {
1700
- term: { 'mappingInfo.dueDate': todayDateString },
1701
- } );
1702
- }
1703
- dueTodayQuery.size = 0;
1704
- const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1705
- dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1683
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1684
+ clQ.size = 0;
1685
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1686
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1706
1687
 
1688
+ let dueToday = 0;
1689
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1690
+
1691
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1692
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1693
+ // Locate nested mappingInfo query
1694
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1695
+ if ( nestedDue ) {
1696
+ // Remove any previous mappingInfo.dueDate term
1697
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1698
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1699
+ );
1700
+ // Add new dueDate filter
1701
+ nestedDue.nested.query.bool.must.push( {
1702
+ term: { 'mappingInfo.dueDate': todayDateString },
1703
+ } );
1704
+ }
1705
+ dueTodayQuery.size = 0;
1706
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1707
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1707
1708
 
1708
- // filter expired Tickets
1709
- let expiredTickets = 0;
1709
+ // filter expired Tickets
1710
+ let expiredTickets = 0;
1710
1711
 
1711
- let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1712
- eQ.size = 0;
1713
- const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1714
- expiredTickets = eResp?.body?.hits?.total?.value || 0;
1712
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1713
+ eQ.size = 0;
1714
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1715
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1715
1716
 
1716
- // ticketAccuracyAbove: avg of revicedPerc > 85%
1717
- let ticketAccuracyAbove = 0;
1717
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1718
1718
 
1719
+ // Build aggregation query for ticket percentage
1720
+ let ticketPercentageAvg = 0;
1719
1721
 
1720
- // For this, add a filter on revicedPerc >= 85
1721
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1722
- {
1723
- script: {
1724
- script: {
1725
- lang: 'painless',
1726
- source: `
1727
- doc['revicedPerc.keyword'].size()!=0 &&
1728
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1729
- `,
1730
- params: { num: 85 },
1722
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1723
+ avgTicketPercentageQuery.size = 0;
1724
+ avgTicketPercentageQuery.aggs = {
1725
+ avg_ticket_percentage: {
1726
+ avg: {
1727
+ script: {
1728
+ source: `
1729
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1730
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1731
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1732
+ } else {
1733
+ return null;
1734
+ }
1735
+ `,
1736
+ lang: 'painless',
1737
+ },
1731
1738
  },
1732
1739
  },
1733
- },
1734
-
1735
-
1736
- ] );
1737
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1738
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1740
+ };
1739
1741
 
1740
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1741
- let ticketAccuracyBelow = 0;
1742
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1742
1743
 
1743
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1744
- {
1745
- script: {
1746
- script: {
1747
- lang: 'painless',
1748
- source: `
1749
- doc['revicedPerc.keyword'].size()!=0 &&
1750
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1751
- `,
1752
- params: { num: 85 },
1753
- },
1754
- },
1755
- },
1744
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1756
1745
 
1757
- ] );
1758
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1759
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1746
+ logger.info( { avgTicketPercentageResp } );
1747
+ // ticketAccuracyBelow: avg of revicedPerc < 85%
1748
+ let ticketAccuracy = 0;
1760
1749
 
1761
- // Final result object
1762
- result = {
1763
- totalTickets,
1764
- openTickets,
1765
- inprogress,
1766
- closedTickets,
1767
- dueToday: dueToday,
1768
- Expired: expiredTickets,
1769
- avgTicket: ticketAccuracyAbove+'%',
1770
- avgAccuracy: ticketAccuracyBelow+'%',
1771
- };
1772
- } else {
1773
- const storeQuery = {
1774
- size: 0,
1775
- query: {
1776
- bool: {
1777
- must: [
1778
- {
1779
- 'range': {
1780
- 'dateString': {
1781
- 'gte': inputData?.fromDate,
1782
- 'lte': inputData?.toDate,
1783
- 'format': 'yyyy-MM-dd',
1750
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1751
+ logger.info( { belowQ } );
1752
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1753
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1754
+ logger.info( { accuracyResp } );
1755
+ // Final result object
1756
+ result = {
1757
+ totalTickets,
1758
+ openTickets,
1759
+ inprogress,
1760
+ closedTickets,
1761
+ dueToday: dueToday,
1762
+ Expired: expiredTickets,
1763
+ avgTicket: ticketPercentageAvg+'%',
1764
+ avgAccuracy: ticketAccuracy+'%',
1765
+ };
1766
+ } else {
1767
+ const storeQuery = {
1768
+ size: 0,
1769
+ query: {
1770
+ bool: {
1771
+ must: [
1772
+ {
1773
+ 'range': {
1774
+ 'dateString': {
1775
+ 'gte': inputData?.fromDate,
1776
+ 'lte': inputData?.toDate,
1777
+ 'format': 'yyyy-MM-dd',
1778
+ },
1784
1779
  },
1785
1780
  },
1786
- },
1787
- {
1788
- nested: {
1789
- path: 'mappingInfo',
1790
- query: {
1791
- bool: {
1792
- must: [
1793
- {
1794
- term: {
1795
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1781
+ {
1782
+ terms: {
1783
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1784
+ inputData.clientId :
1785
+ [ inputData.clientId ],
1786
+ },
1787
+
1788
+ },
1789
+ {
1790
+ nested: {
1791
+ path: 'mappingInfo',
1792
+ query: {
1793
+ bool: {
1794
+ must: [
1795
+ {
1796
+ term: {
1797
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1798
+ },
1796
1799
  },
1797
- },
1798
- ],
1800
+ ],
1801
+ },
1799
1802
  },
1800
1803
  },
1801
1804
  },
1802
- },
1803
1805
 
1804
- ],
1806
+ ],
1807
+ },
1805
1808
  },
1806
- },
1807
- };
1809
+ };
1808
1810
 
1809
- // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1810
- function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1811
- let q = JSON.parse( JSON.stringify( baseQuery ) );
1812
- // Remove any previous mappingInfo.status term
1813
- let nested = q.query.bool.must.find( ( m ) => m.nested );
1814
- if ( nested ) {
1815
- // filter out all mappingInfo.status
1816
- nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1817
- return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1818
- } );
1819
- // add desired status
1820
- nested.nested.query.bool.must.push( {
1821
- term: {
1822
- 'mappingInfo.status': statusValue,
1823
- },
1824
- } );
1811
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1812
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1813
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1814
+ // Remove any previous mappingInfo.status term
1815
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1816
+ if ( nested ) {
1817
+ // filter out all mappingInfo.status
1818
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1819
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1820
+ } );
1821
+ // add desired status
1822
+ nested.nested.query.bool.must.push( {
1823
+ term: {
1824
+ 'mappingInfo.status': statusValue,
1825
+ },
1826
+ } );
1827
+ }
1828
+ return q;
1825
1829
  }
1826
- return q;
1827
- }
1828
1830
 
1829
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1830
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1831
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1832
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1831
1833
 
1832
- // locate nested section
1833
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1834
+ // locate nested section
1835
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1834
1836
 
1835
- if ( nested ) {
1836
- // remove old status filters
1837
- nested.nested.query.bool.must =
1838
- nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1837
+ if ( nested ) {
1838
+ // remove old status filters
1839
+ nested.nested.query.bool.must =
1840
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1839
1841
 
1840
- // add new filters
1841
- nested.nested.query.bool.must.push( ...filters );
1842
- }
1842
+ // add new filters
1843
+ nested.nested.query.bool.must.push( ...filters );
1844
+ }
1843
1845
 
1844
- return {
1845
- ...q,
1846
- size: 0,
1847
- aggs: {
1848
- avg_value: {
1849
- avg: {
1850
- script: {
1851
- lang: 'painless',
1852
- source: `
1853
- if (doc['revicedPerc.keyword'].size() == 0) return null;
1854
- String v = doc['revicedPerc.keyword'].value.replace('%','');
1855
- try {
1856
- return Double.parseDouble(v);
1857
- } catch (Exception e) {
1858
- return null;
1859
- }
1860
- `,
1861
- },
1846
+ return {
1847
+ ...q,
1848
+ size: 0,
1849
+ aggs: {
1850
+ avg_value: {
1851
+ avg: 'mappingInfp.reviced',
1862
1852
  },
1863
1853
  },
1864
- },
1854
+ };
1865
1855
  };
1866
- };
1867
-
1868
1856
 
1869
- // Get OpenSearch connection
1870
1857
 
1871
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1858
+ // Get OpenSearch connection
1872
1859
 
1873
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1874
- let totalTickets = 0;
1860
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1875
1861
 
1876
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1862
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1863
+ let totalTickets = 0;
1877
1864
 
1878
- allQuery.size = 0;
1879
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1880
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1865
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1881
1866
 
1882
- // openTickets: mappingInfo.status: 'Open'
1883
- let openTickets = 0;
1867
+ allQuery.size = 0;
1868
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1869
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1884
1870
 
1885
- let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1886
- otQ.size = 0;
1887
- const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1888
- openTickets = openResp?.body?.hits?.total?.value || 0;
1889
- // logger.info( { msd: '..............2', openResp } );
1871
+ // openTickets: mappingInfo.status: 'Open'
1872
+ let openTickets = 0;
1890
1873
 
1874
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1875
+ otQ.size = 0;
1876
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1877
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1878
+ // logger.info( { msd: '..............2', openResp } );
1891
1879
 
1892
- // inprogress: mappingInfo.status: 'in-Progress'
1893
- let inprogress = 0;
1894
1880
 
1895
- let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1896
- ipQ.size = 0;
1897
- const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1898
- inprogress = ipResp?.body?.hits?.total?.value || 0;
1881
+ // inprogress: mappingInfo.status: 'in-Progress'
1882
+ let inprogress = 0;
1899
1883
 
1900
- // closedTickets: mappingInfo.status: 'closed'
1901
- let closedTickets = 0;
1884
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1885
+ ipQ.size = 0;
1886
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1887
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1902
1888
 
1903
- let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1904
- clQ.size = 0;
1905
- const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1906
- closedTickets = clResp?.body?.hits?.total?.value || 0;
1907
- // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1908
- let dueToday = 0;
1909
- let todayDateString = new Date().toISOString().slice( 0, 10 );
1889
+ // closedTickets: mappingInfo.status: 'closed'
1890
+ let closedTickets = 0;
1910
1891
 
1911
- // Build a query for tickets with mappingInfo.dueDate == todayDateString
1912
- let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1913
- // Locate nested mappingInfo query
1914
- let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1915
- if ( nestedDue ) {
1916
- // Remove any previous mappingInfo.dueDate term
1917
- nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1918
- ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1919
- );
1920
- // Add new dueDate filter
1921
- nestedDue.nested.query.bool.must.push( {
1922
- term: { 'mappingInfo.dueDate': todayDateString },
1923
- } );
1924
- }
1925
- dueTodayQuery.size = 0;
1926
- const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1927
- dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1892
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1893
+ clQ.size = 0;
1894
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1895
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1896
+ // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1897
+ let dueToday = 0;
1898
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1899
+
1900
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1901
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1902
+ // Locate nested mappingInfo query
1903
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1904
+ if ( nestedDue ) {
1905
+ // Remove any previous mappingInfo.dueDate term
1906
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1907
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1908
+ );
1909
+ // Add new dueDate filter
1910
+ nestedDue.nested.query.bool.must.push( {
1911
+ term: { 'mappingInfo.dueDate': todayDateString },
1912
+ } );
1913
+ }
1914
+ dueTodayQuery.size = 0;
1915
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1916
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1928
1917
 
1929
1918
 
1930
- // filter expired Tickets
1931
- let expiredTickets = 0;
1919
+ // filter expired Tickets
1920
+ let expiredTickets = 0;
1932
1921
 
1933
- let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1934
- eQ.size = 0;
1935
- const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1936
- expiredTickets = eResp?.body?.hits?.total?.value || 0;
1922
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1923
+ eQ.size = 0;
1924
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1925
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1937
1926
 
1938
- // filter under tango review
1927
+ // filter under tango review
1939
1928
 
1940
- let undertangoTickets = 0;
1929
+ let undertangoTickets = 0;
1941
1930
 
1942
- let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1943
- utrQ.size = 0;
1944
- const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1945
- undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1931
+ let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1932
+ utrQ.size = 0;
1933
+ const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1934
+ undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1946
1935
 
1947
- // For this, add a filter on revicedPerc >= 85
1948
- let ticketAccuracyAbove = 0;
1949
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1950
- {
1951
- script: {
1952
- script: {
1953
- lang: 'painless',
1954
- source: `
1955
- doc['revicedPerc.keyword'].size()!=0 &&
1956
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1957
- `,
1958
- params: { num: 85 },
1936
+ let ticketPercentageAvg =0;
1937
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1938
+ avgTicketPercentageQuery.size = 0;
1939
+ avgTicketPercentageQuery.aggs = {
1940
+ avg_ticket_percentage: {
1941
+ avg: {
1942
+ script: {
1943
+ source: `
1944
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1945
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1946
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1947
+ } else {
1948
+ return null;
1949
+ }
1950
+ `,
1951
+ lang: 'painless',
1952
+ },
1959
1953
  },
1960
1954
  },
1961
- },
1962
-
1963
-
1964
- ] );
1965
-
1966
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1967
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1968
-
1969
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1970
- let ticketAccuracyBelow = 0;
1955
+ };
1971
1956
 
1972
- let belowQ = buildAggStoreQuery( baseStoreQuery, [
1973
- {
1974
- script: {
1975
- script: {
1976
- lang: 'painless',
1977
- source: `
1978
- doc['revicedPerc.keyword'].size()!=0 &&
1979
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
1980
- `,
1981
- params: { num: 85 },
1982
- },
1983
- },
1984
- },
1957
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1985
1958
 
1986
- ] );
1987
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1988
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1959
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1960
+ logger.info( { avgTicketPercentageResp } );
1961
+ let ticketAccuracy = 0;
1989
1962
 
1990
- // Final result object
1991
- result = {
1992
- totalTickets,
1993
- openTickets,
1994
- inprogress,
1995
- closedTickets,
1996
- dueToday: dueToday,
1997
- Expired: expiredTickets,
1998
- underTangoReview: undertangoTickets,
1999
- avgTicket: ticketAccuracyAbove+'%',
2000
- avgAccuracy: ticketAccuracyBelow+'%',
2001
- };
1963
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1964
+ logger.info( { belowQ } );
1965
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1966
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1967
+ logger.info( { accuracyResp } );
1968
+ // Final result object
1969
+ result = {
1970
+ totalTickets,
1971
+ openTickets,
1972
+ inprogress,
1973
+ closedTickets,
1974
+ dueToday: dueToday,
1975
+ Expired: expiredTickets,
1976
+ underTangoReview: undertangoTickets,
1977
+ avgTicket: ticketPercentageAvg+'%',
1978
+ avgAccuracy: ticketAccuracy+'%',
1979
+ };
1980
+ }
2002
1981
  }
2003
1982
  }
2004
1983
 
@@ -4925,7 +4904,7 @@ export async function updateUserTicketStatus( req, res ) {
4925
4904
 
4926
4905
  const updatePayload = {
4927
4906
  doc: {
4928
- status: 'In-Progress',
4907
+ status: lastEntry?.type == 'review'? 'Reviewer In progress':lastEntry?.type == 'approve'? 'Approver In progress':ticketSource?.status,
4929
4908
  mappingInfo: updatedMappingInfo,
4930
4909
  updatedAt: currentTime,
4931
4910
  },