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

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.86",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -902,7 +902,11 @@ 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
911
  if ( req?.user?.userType === 'tango' ) {
908
912
  switch ( inputData?.tangoType ) {
@@ -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,7 +1340,7 @@ 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' ) {
1328
1344
  if ( ticketsFeature && !ticketsApproveFeature ) {
1329
1345
  const storeQuery = {
1330
1346
  size: 0,
@@ -1340,6 +1356,14 @@ export async function ticketSummary( req, res ) {
1340
1356
  },
1341
1357
  },
1342
1358
  },
1359
+ {
1360
+ terms: {
1361
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1362
+ inputData.clientId :
1363
+ [ inputData.clientId ],
1364
+ },
1365
+
1366
+ },
1343
1367
  {
1344
1368
  nested: {
1345
1369
  path: 'mappingInfo',
@@ -1402,20 +1426,7 @@ export async function ticketSummary( req, res ) {
1402
1426
  size: 0,
1403
1427
  aggs: {
1404
1428
  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
- },
1429
+ avg: 'mappingInfo.reviced',
1419
1430
  },
1420
1431
  },
1421
1432
  };
@@ -1490,48 +1501,44 @@ try {
1490
1501
  const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1491
1502
  expiredTickets = eResp?.body?.hits?.total?.value || 0;
1492
1503
 
1493
- let ticketAccuracyAbove = 0;
1494
- // For this, add a filter on revicedPerc >= 85
1495
- let aboveQ = buildAggStoreQuery( baseStoreQuery, [
1496
- {
1497
- script: {
1504
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1505
+
1506
+ // Build aggregation query for ticket percentage
1507
+ let ticketPercentageAvg = 0;
1508
+
1509
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1510
+ avgTicketPercentageQuery.size = 0;
1511
+ avgTicketPercentageQuery.aggs = {
1512
+ avg_ticket_percentage: {
1513
+ avg: {
1498
1514
  script: {
1499
- lang: 'painless',
1500
1515
  source: `
1501
- doc['revicedPerc.keyword'].size()!=0 &&
1502
- Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
1516
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1517
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1518
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1519
+ } else {
1520
+ return null;
1521
+ }
1503
1522
  `,
1504
- params: { num: 85 },
1523
+ lang: 'painless',
1505
1524
  },
1506
1525
  },
1507
1526
  },
1527
+ };
1508
1528
 
1529
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1509
1530
 
1510
- ] );
1511
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1512
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1531
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1513
1532
 
1533
+ logger.info( { avgTicketPercentageResp } );
1514
1534
  // 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';
1535
+ let ticketAccuracy = 0;
1534
1536
 
1537
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1538
+ logger.info( { belowQ } );
1539
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1540
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1541
+ logger.info( { accuracyResp } );
1535
1542
  // Final result object
1536
1543
  result = {
1537
1544
  totalTickets,
@@ -1540,465 +1547,434 @@ try {
1540
1547
  closedTickets,
1541
1548
  dueToday: dueToday,
1542
1549
  Expired: expiredTickets,
1543
- avgTicket: ticketAccuracyAbove+'%',
1544
- avgAccuracy: ticketAccuracyBelow+'%',
1550
+ avgTicket: ticketPercentageAvg+'%',
1551
+ avgAccuracy: ticketAccuracy+'%',
1545
1552
  };
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',
1553
+ } else if ( ticketsFeature && ticketsApproveFeature ) {
1554
+ if ( inputData?.permissionType === 'review' ) {
1555
+ const storeQuery = {
1556
+ size: 0,
1557
+ query: {
1558
+ bool: {
1559
+ must: [
1560
+ {
1561
+ 'range': {
1562
+ 'dateString': {
1563
+ 'gte': inputData?.fromDate,
1564
+ 'lte': inputData?.toDate,
1565
+ 'format': 'yyyy-MM-dd',
1566
+ },
1560
1567
  },
1561
1568
  },
1562
- },
1563
- {
1564
- nested: {
1565
- path: 'mappingInfo',
1566
- query: {
1567
- bool: {
1568
- must: [
1569
- {
1570
- term: {
1571
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1569
+ {
1570
+ terms: {
1571
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1572
+ inputData.clientId :
1573
+ [ inputData.clientId ],
1574
+ },
1575
+
1576
+ },
1577
+ {
1578
+ nested: {
1579
+ path: 'mappingInfo',
1580
+ query: {
1581
+ bool: {
1582
+ must: [
1583
+ {
1584
+ term: {
1585
+ 'mappingInfo.type': 'review',
1586
+ },
1572
1587
  },
1573
- },
1574
- ],
1588
+ ],
1589
+ },
1575
1590
  },
1576
1591
  },
1577
1592
  },
1578
- },
1579
1593
 
1580
- ],
1594
+ ],
1595
+ },
1581
1596
  },
1582
- },
1583
- };
1597
+ };
1584
1598
 
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
- } );
1599
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1600
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1601
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1602
+ // Remove any previous mappingInfo.status term
1603
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1604
+ if ( nested ) {
1605
+ // filter out all mappingInfo.status
1606
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1607
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1608
+ } );
1609
+ // add desired status
1610
+ nested.nested.query.bool.must.push( {
1611
+ term: {
1612
+ 'mappingInfo.status': statusValue,
1613
+ },
1614
+ } );
1615
+ }
1616
+ return q;
1601
1617
  }
1602
- return q;
1603
- }
1604
1618
 
1605
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1606
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1619
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1620
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1607
1621
 
1608
- // locate nested section
1609
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1622
+ // locate nested section
1623
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1610
1624
 
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'] ) );
1625
+ if ( nested ) {
1626
+ // remove old status filters
1627
+ nested.nested.query.bool.must =
1628
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1615
1629
 
1616
- // add new filters
1617
- nested.nested.query.bool.must.push( ...filters );
1618
- }
1630
+ // add new filters
1631
+ nested.nested.query.bool.must.push( ...filters );
1632
+ }
1619
1633
 
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
- },
1634
+ return {
1635
+ ...q,
1636
+ size: 0,
1637
+ aggs: {
1638
+ avg_value: {
1639
+ avg: 'mappingInfo.reviced',
1638
1640
  },
1639
1641
  },
1640
- },
1642
+ };
1641
1643
  };
1642
- };
1643
1644
 
1644
1645
 
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;
1646
+ // Get OpenSearch connection
1651
1647
 
1652
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1648
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1653
1649
 
1654
- allQuery.size = 0;
1655
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1656
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1650
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1651
+ let totalTickets = 0;
1657
1652
 
1658
- // openTickets: mappingInfo.status: 'Open'
1659
- let openTickets = 0;
1653
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1660
1654
 
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 } );
1655
+ allQuery.size = 0;
1656
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1657
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1666
1658
 
1659
+ // openTickets: mappingInfo.status: 'Open'
1660
+ let openTickets = 0;
1667
1661
 
1668
- // inprogress: mappingInfo.status: 'in-Progress'
1669
- let inprogress = 0;
1662
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1663
+ otQ.size = 0;
1664
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1665
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1666
+ // logger.info( { msd: '..............2', openResp } );
1670
1667
 
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
1668
 
1676
- // closedTickets: mappingInfo.status: 'closed'
1677
- let closedTickets = 0;
1669
+ // inprogress: mappingInfo.status: 'in-Progress'
1670
+ let inprogress = 0;
1678
1671
 
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)
1672
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1673
+ ipQ.size = 0;
1674
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1675
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1684
1676
 
1685
- // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1686
- let dueToday = 0;
1687
- let todayDateString = new Date().toISOString().slice( 0, 10 );
1677
+ // closedTickets: mappingInfo.status: 'closed'
1678
+ let closedTickets = 0;
1688
1679
 
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;
1680
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1681
+ clQ.size = 0;
1682
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1683
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1706
1684
 
1685
+ let dueToday = 0;
1686
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1687
+
1688
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1689
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1690
+ // Locate nested mappingInfo query
1691
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1692
+ if ( nestedDue ) {
1693
+ // Remove any previous mappingInfo.dueDate term
1694
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1695
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1696
+ );
1697
+ // Add new dueDate filter
1698
+ nestedDue.nested.query.bool.must.push( {
1699
+ term: { 'mappingInfo.dueDate': todayDateString },
1700
+ } );
1701
+ }
1702
+ dueTodayQuery.size = 0;
1703
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1704
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1707
1705
 
1708
- // filter expired Tickets
1709
- let expiredTickets = 0;
1706
+ // filter expired Tickets
1707
+ let expiredTickets = 0;
1710
1708
 
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;
1709
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1710
+ eQ.size = 0;
1711
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1712
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1715
1713
 
1716
- // ticketAccuracyAbove: avg of revicedPerc > 85%
1717
- let ticketAccuracyAbove = 0;
1714
+ // Calculate average ticket percentage: avg((reviced/footfallCount)*100) filtered by baseStoreQuery
1718
1715
 
1716
+ // Build aggregation query for ticket percentage
1717
+ let ticketPercentageAvg = 0;
1719
1718
 
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 },
1719
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1720
+ avgTicketPercentageQuery.size = 0;
1721
+ avgTicketPercentageQuery.aggs = {
1722
+ avg_ticket_percentage: {
1723
+ avg: {
1724
+ script: {
1725
+ source: `
1726
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1727
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1728
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1729
+ } else {
1730
+ return null;
1731
+ }
1732
+ `,
1733
+ lang: 'painless',
1734
+ },
1731
1735
  },
1732
1736
  },
1733
- },
1734
-
1735
-
1736
- ] );
1737
- const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
1738
- ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1737
+ };
1739
1738
 
1740
- // ticketAccuracyBelow: avg of revicedPerc < 85%
1741
- let ticketAccuracyBelow = 0;
1739
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1742
1740
 
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
- },
1741
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1756
1742
 
1757
- ] );
1758
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1759
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1743
+ logger.info( { avgTicketPercentageResp } );
1744
+ // ticketAccuracyBelow: avg of revicedPerc < 85%
1745
+ let ticketAccuracy = 0;
1760
1746
 
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',
1747
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1748
+ logger.info( { belowQ } );
1749
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1750
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1751
+ logger.info( { accuracyResp } );
1752
+ // Final result object
1753
+ result = {
1754
+ totalTickets,
1755
+ openTickets,
1756
+ inprogress,
1757
+ closedTickets,
1758
+ dueToday: dueToday,
1759
+ Expired: expiredTickets,
1760
+ avgTicket: ticketPercentageAvg+'%',
1761
+ avgAccuracy: ticketAccuracy+'%',
1762
+ };
1763
+ } else {
1764
+ const storeQuery = {
1765
+ size: 0,
1766
+ query: {
1767
+ bool: {
1768
+ must: [
1769
+ {
1770
+ 'range': {
1771
+ 'dateString': {
1772
+ 'gte': inputData?.fromDate,
1773
+ 'lte': inputData?.toDate,
1774
+ 'format': 'yyyy-MM-dd',
1775
+ },
1784
1776
  },
1785
1777
  },
1786
- },
1787
- {
1788
- nested: {
1789
- path: 'mappingInfo',
1790
- query: {
1791
- bool: {
1792
- must: [
1793
- {
1794
- term: {
1795
- 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1778
+ {
1779
+ terms: {
1780
+ 'clientId.keyword': Array.isArray( inputData.clientId ) ?
1781
+ inputData.clientId :
1782
+ [ inputData.clientId ],
1783
+ },
1784
+
1785
+ },
1786
+ {
1787
+ nested: {
1788
+ path: 'mappingInfo',
1789
+ query: {
1790
+ bool: {
1791
+ must: [
1792
+ {
1793
+ term: {
1794
+ 'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
1795
+ },
1796
1796
  },
1797
- },
1798
- ],
1797
+ ],
1798
+ },
1799
1799
  },
1800
1800
  },
1801
1801
  },
1802
- },
1803
1802
 
1804
- ],
1803
+ ],
1804
+ },
1805
1805
  },
1806
- },
1807
- };
1806
+ };
1808
1807
 
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
- } );
1808
+ // Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
1809
+ function buildStoreQueryWithStatus( baseQuery, statusValue ) {
1810
+ let q = JSON.parse( JSON.stringify( baseQuery ) );
1811
+ // Remove any previous mappingInfo.status term
1812
+ let nested = q.query.bool.must.find( ( m ) => m.nested );
1813
+ if ( nested ) {
1814
+ // filter out all mappingInfo.status
1815
+ nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
1816
+ return !( mustItem.term && mustItem.term['mappingInfo.status'] );
1817
+ } );
1818
+ // add desired status
1819
+ nested.nested.query.bool.must.push( {
1820
+ term: {
1821
+ 'mappingInfo.status': statusValue,
1822
+ },
1823
+ } );
1824
+ }
1825
+ return q;
1825
1826
  }
1826
- return q;
1827
- }
1828
1827
 
1829
- const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1830
- const q = JSON.parse( JSON.stringify( baseQuery ) );
1828
+ const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
1829
+ const q = JSON.parse( JSON.stringify( baseQuery ) );
1831
1830
 
1832
- // locate nested section
1833
- const nested = q.query.bool.must.find( ( m ) => m.nested );
1831
+ // locate nested section
1832
+ const nested = q.query.bool.must.find( ( m ) => m.nested );
1834
1833
 
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'] ) );
1834
+ if ( nested ) {
1835
+ // remove old status filters
1836
+ nested.nested.query.bool.must =
1837
+ nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
1839
1838
 
1840
- // add new filters
1841
- nested.nested.query.bool.must.push( ...filters );
1842
- }
1839
+ // add new filters
1840
+ nested.nested.query.bool.must.push( ...filters );
1841
+ }
1843
1842
 
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
- },
1843
+ return {
1844
+ ...q,
1845
+ size: 0,
1846
+ aggs: {
1847
+ avg_value: {
1848
+ avg: 'mappingInfp.reviced',
1862
1849
  },
1863
1850
  },
1864
- },
1851
+ };
1865
1852
  };
1866
- };
1867
-
1868
1853
 
1869
- // Get OpenSearch connection
1870
1854
 
1871
- const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1855
+ // Get OpenSearch connection
1872
1856
 
1873
- // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1874
- let totalTickets = 0;
1857
+ const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
1875
1858
 
1876
- let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1859
+ // Total Tickets (all tickets with mappingInfo.type == tangoreview)
1860
+ let totalTickets = 0;
1877
1861
 
1878
- allQuery.size = 0;
1879
- const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1880
- totalTickets = totalResp?.body?.hits?.total?.value || 0;
1862
+ let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1881
1863
 
1882
- // openTickets: mappingInfo.status: 'Open'
1883
- let openTickets = 0;
1864
+ allQuery.size = 0;
1865
+ const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
1866
+ totalTickets = totalResp?.body?.hits?.total?.value || 0;
1884
1867
 
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 } );
1868
+ // openTickets: mappingInfo.status: 'Open'
1869
+ let openTickets = 0;
1890
1870
 
1871
+ let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
1872
+ otQ.size = 0;
1873
+ const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
1874
+ openTickets = openResp?.body?.hits?.total?.value || 0;
1875
+ // logger.info( { msd: '..............2', openResp } );
1891
1876
 
1892
- // inprogress: mappingInfo.status: 'in-Progress'
1893
- let inprogress = 0;
1894
1877
 
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;
1878
+ // inprogress: mappingInfo.status: 'in-Progress'
1879
+ let inprogress = 0;
1899
1880
 
1900
- // closedTickets: mappingInfo.status: 'closed'
1901
- let closedTickets = 0;
1881
+ let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
1882
+ ipQ.size = 0;
1883
+ const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
1884
+ inprogress = ipResp?.body?.hits?.total?.value || 0;
1902
1885
 
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 );
1886
+ // closedTickets: mappingInfo.status: 'closed'
1887
+ let closedTickets = 0;
1910
1888
 
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;
1889
+ let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
1890
+ clQ.size = 0;
1891
+ const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
1892
+ closedTickets = clResp?.body?.hits?.total?.value || 0;
1893
+ // dueToday: Tickets whose dueDate is today (format 'yyyy-MM-dd')
1894
+ let dueToday = 0;
1895
+ let todayDateString = new Date().toISOString().slice( 0, 10 );
1896
+
1897
+ // Build a query for tickets with mappingInfo.dueDate == todayDateString
1898
+ let dueTodayQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1899
+ // Locate nested mappingInfo query
1900
+ let nestedDue = dueTodayQuery.query.bool.must.find( ( m ) => m.nested );
1901
+ if ( nestedDue ) {
1902
+ // Remove any previous mappingInfo.dueDate term
1903
+ nestedDue.nested.query.bool.must = nestedDue.nested.query.bool.must.filter(
1904
+ ( mustItem ) => !( mustItem.term && mustItem.term['mappingInfo.dueDate'] ),
1905
+ );
1906
+ // Add new dueDate filter
1907
+ nestedDue.nested.query.bool.must.push( {
1908
+ term: { 'mappingInfo.dueDate': todayDateString },
1909
+ } );
1910
+ }
1911
+ dueTodayQuery.size = 0;
1912
+ const dueTodayResp = await getOpenSearchData( openSearch.footfallDirectory, dueTodayQuery );
1913
+ dueToday = dueTodayResp?.body?.hits?.total?.value || 0;
1928
1914
 
1929
1915
 
1930
- // filter expired Tickets
1931
- let expiredTickets = 0;
1916
+ // filter expired Tickets
1917
+ let expiredTickets = 0;
1932
1918
 
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;
1919
+ let eQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1920
+ eQ.size = 0;
1921
+ const eResp = await getOpenSearchData( openSearch.footfallDirectory, eQ );
1922
+ expiredTickets = eResp?.body?.hits?.total?.value || 0;
1937
1923
 
1938
- // filter under tango review
1924
+ // filter under tango review
1939
1925
 
1940
- let undertangoTickets = 0;
1926
+ let undertangoTickets = 0;
1941
1927
 
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;
1928
+ let utrQ = buildStoreQueryWithStatus( baseStoreQuery, 'Under Tango Review' );
1929
+ utrQ.size = 0;
1930
+ const utrResp = await getOpenSearchData( openSearch.footfallDirectory, utrQ );
1931
+ undertangoTickets = utrResp?.body?.hits?.total?.value || 0;
1946
1932
 
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 },
1933
+ let ticketPercentageAvg =0;
1934
+ let avgTicketPercentageQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
1935
+ avgTicketPercentageQuery.size = 0;
1936
+ avgTicketPercentageQuery.aggs = {
1937
+ avg_ticket_percentage: {
1938
+ avg: {
1939
+ script: {
1940
+ source: `
1941
+ if (doc.containsKey('reviced') && doc['reviced'].size()!=0 &&
1942
+ doc.containsKey('footfallCount') && doc['footfallCount'].size()!=0 && doc['footfallCount'].value != 0) {
1943
+ return (doc['reviced'].value / doc['footfallCount'].value) * 100;
1944
+ } else {
1945
+ return null;
1946
+ }
1947
+ `,
1948
+ lang: 'painless',
1949
+ },
1959
1950
  },
1960
1951
  },
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;
1952
+ };
1971
1953
 
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
- },
1954
+ const avgTicketPercentageResp = await getOpenSearchData( openSearch.footfallDirectory, avgTicketPercentageQuery );
1985
1955
 
1986
- ] );
1987
- const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1988
- ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1956
+ ticketPercentageAvg = avgTicketPercentageResp?.body?.aggregations?.avg_ticket_percentage?.value?.toFixed( 2 ) || '0';
1957
+ logger.info( { avgTicketPercentageResp } );
1958
+ let ticketAccuracy = 0;
1989
1959
 
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
- };
1960
+ let belowQ = buildAggStoreQuery( baseStoreQuery );
1961
+ logger.info( { belowQ } );
1962
+ const accuracyResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
1963
+ ticketAccuracy = accuracyResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
1964
+ logger.info( { accuracyResp } );
1965
+ // Final result object
1966
+ result = {
1967
+ totalTickets,
1968
+ openTickets,
1969
+ inprogress,
1970
+ closedTickets,
1971
+ dueToday: dueToday,
1972
+ Expired: expiredTickets,
1973
+ underTangoReview: undertangoTickets,
1974
+ avgTicket: ticketPercentageAvg+'%',
1975
+ avgAccuracy: ticketAccuracy+'%',
1976
+ };
1977
+ }
2002
1978
  }
2003
1979
  }
2004
1980
 
@@ -4925,7 +4901,7 @@ export async function updateUserTicketStatus( req, res ) {
4925
4901
 
4926
4902
  const updatePayload = {
4927
4903
  doc: {
4928
- status: 'In-Progress',
4904
+ status: lastEntry?.type == 'review'? 'Reviewer In progress':lastEntry?.type == 'approve'? 'Approver In progress':ticketSource?.status,
4929
4905
  mappingInfo: updatedMappingInfo,
4930
4906
  updatedAt: currentTime,
4931
4907
  },