tango-app-api-infra 3.9.5-vms.76 → 3.9.5-vms.78
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,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-infra",
|
|
3
|
-
"version": "3.9.5-vms.
|
|
3
|
+
"version": "3.9.5-vms.78",
|
|
4
4
|
"description": "infra",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"start": "nodemon --exec \"eslint --fix . && node
|
|
8
|
+
"start": "nodemon --exec \"eslint --fix . && node index.js\""
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=18.10.0"
|
|
@@ -86,6 +86,10 @@ export async function createinternalTicket( req, res ) {
|
|
|
86
86
|
mappingInfo: [],
|
|
87
87
|
};
|
|
88
88
|
const id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
89
|
+
let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
90
|
+
if ( getExistingOne?.body?._source ) {
|
|
91
|
+
return res.sendError( 'Ticket Already Exists', 500 );
|
|
92
|
+
}
|
|
89
93
|
const insertResult = await insertWithId( openSearch.footfallDirectory, id, record );
|
|
90
94
|
if ( insertResult && insertResult.statusCode === 201 ) {
|
|
91
95
|
return res.sendSuccess( 'Ticket raised successfully' );
|
|
@@ -380,7 +384,8 @@ export async function tangoReviewTicket( req, res ) {
|
|
|
380
384
|
// return;
|
|
381
385
|
|
|
382
386
|
let id = `${inputData.storeId}_${inputData.dateString}_footfall-directory-tagging`;
|
|
383
|
-
|
|
387
|
+
let getExistingOne = await getOpenSearchById( openSearch.footfallDirectory, id );
|
|
388
|
+
if ( inputData.ticketType === 'internal' &&!getExistingOne?.body?._source ) {
|
|
384
389
|
id = `${inputData.storeId}_${inputData.dateString}_internal_footfall-directory-tagging`;
|
|
385
390
|
}
|
|
386
391
|
|
|
@@ -718,20 +723,858 @@ export async function ticketSummary1( req, res ) {
|
|
|
718
723
|
|
|
719
724
|
export async function ticketSummary( req, res ) {
|
|
720
725
|
try {
|
|
726
|
+
const inputData = req.query;
|
|
727
|
+
|
|
721
728
|
let result = '';
|
|
729
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
722
730
|
const userInfo = req.user;
|
|
723
731
|
const ticketsFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'reviewer' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
732
|
+
const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
733
|
+
|
|
724
734
|
// const ticketsApproveFeature = userInfo?.rolespermission?.some( ( f ) => f.featureName === 'FootfallDirectory' && ( f.modules.find( ( m ) => m.name == 'approver' && ( m.isAdd == true || m.isEdit == true ) ) ) );
|
|
725
|
-
if ( req
|
|
735
|
+
if ( req?.user?.userType === 'tango' ) {
|
|
736
|
+
switch ( inputData?.tangoType ) {
|
|
737
|
+
case 'store':
|
|
738
|
+
const storeQuery = {
|
|
739
|
+
size: 0,
|
|
740
|
+
query: {
|
|
741
|
+
bool: {
|
|
742
|
+
must: [
|
|
743
|
+
{
|
|
744
|
+
'range': {
|
|
745
|
+
'dateString': {
|
|
746
|
+
'gte': inputData?.fromDate,
|
|
747
|
+
'lte': inputData?.toDate,
|
|
748
|
+
'format': 'yyyy-MM-dd',
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
nested: {
|
|
754
|
+
path: 'mappingInfo',
|
|
755
|
+
query: {
|
|
756
|
+
bool: {
|
|
757
|
+
must: [
|
|
758
|
+
{
|
|
759
|
+
term: {
|
|
760
|
+
'mappingInfo.type': 'tangoreview',
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
],
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
|
|
769
|
+
],
|
|
770
|
+
},
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
775
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
776
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
777
|
+
// Remove any previous mappingInfo.status term
|
|
778
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
779
|
+
if ( nested ) {
|
|
780
|
+
// filter out all mappingInfo.status
|
|
781
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
782
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
783
|
+
} );
|
|
784
|
+
// add desired status
|
|
785
|
+
nested.nested.query.bool.must.push( {
|
|
786
|
+
term: {
|
|
787
|
+
'mappingInfo.status': statusValue,
|
|
788
|
+
},
|
|
789
|
+
} );
|
|
790
|
+
}
|
|
791
|
+
return q;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
795
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
796
|
+
|
|
797
|
+
// locate nested section
|
|
798
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
799
|
+
|
|
800
|
+
if ( nested ) {
|
|
801
|
+
// remove old status filters
|
|
802
|
+
nested.nested.query.bool.must =
|
|
803
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
804
|
+
|
|
805
|
+
// add new filters
|
|
806
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
return {
|
|
810
|
+
...q,
|
|
811
|
+
size: 0,
|
|
812
|
+
aggs: {
|
|
813
|
+
avg_value: {
|
|
814
|
+
avg: {
|
|
815
|
+
script: {
|
|
816
|
+
lang: 'painless',
|
|
817
|
+
source: `
|
|
818
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
819
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
820
|
+
try {
|
|
821
|
+
return Double.parseDouble(v);
|
|
822
|
+
} catch (Exception e) {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
`,
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
};
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
// Get OpenSearch connection
|
|
835
|
+
|
|
836
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
837
|
+
|
|
838
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
839
|
+
let totalTickets = 0;
|
|
840
|
+
|
|
841
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
842
|
+
|
|
843
|
+
allQuery.size = 0;
|
|
844
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
845
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
846
|
+
|
|
847
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
848
|
+
let openTickets = 0;
|
|
849
|
+
|
|
850
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
851
|
+
otQ.size = 0;
|
|
852
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
853
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
854
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
858
|
+
let openInfraIssues = 0;
|
|
859
|
+
|
|
860
|
+
let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
|
|
861
|
+
oiQ.size = 0;
|
|
862
|
+
const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
|
|
863
|
+
openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
|
|
864
|
+
|
|
865
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
866
|
+
let inprogress = 0;
|
|
867
|
+
|
|
868
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
869
|
+
ipQ.size = 0;
|
|
870
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
871
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
872
|
+
|
|
873
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
874
|
+
let closedTickets = 0;
|
|
875
|
+
|
|
876
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
877
|
+
clQ.size = 0;
|
|
878
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
879
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
880
|
+
// Average revisedPerc (for all tangoreview)
|
|
881
|
+
let averageAccuracyOverAll = 0;
|
|
882
|
+
|
|
883
|
+
let avgQ = buildAggStoreQuery( baseStoreQuery );
|
|
884
|
+
const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
|
|
885
|
+
averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
886
|
+
|
|
887
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
888
|
+
let ticketAccuracyAbove = 0;
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
// For this, add a filter on revicedPerc >= 85
|
|
892
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
893
|
+
{
|
|
894
|
+
script: {
|
|
895
|
+
script: {
|
|
896
|
+
lang: 'painless',
|
|
897
|
+
source: `
|
|
898
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
899
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
900
|
+
`,
|
|
901
|
+
params: { num: 85 },
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
] );
|
|
908
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
909
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
910
|
+
|
|
911
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
912
|
+
let ticketAccuracyBelow = 0;
|
|
913
|
+
|
|
914
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
915
|
+
{
|
|
916
|
+
script: {
|
|
917
|
+
script: {
|
|
918
|
+
lang: 'painless',
|
|
919
|
+
source: `
|
|
920
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
921
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
922
|
+
`,
|
|
923
|
+
params: { num: 85 },
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
},
|
|
927
|
+
|
|
928
|
+
] );
|
|
929
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
930
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
931
|
+
|
|
932
|
+
// Final result object
|
|
933
|
+
result = {
|
|
934
|
+
totalTickets,
|
|
935
|
+
averageAccuracyOverAll: averageAccuracyOverAll+'%',
|
|
936
|
+
openTickets,
|
|
937
|
+
openInfraIssues,
|
|
938
|
+
inprogress,
|
|
939
|
+
closedTickets,
|
|
940
|
+
ticketAccuracyAbove: ticketAccuracyAbove+'%',
|
|
941
|
+
ticketAccuracyBelow: ticketAccuracyBelow+'%',
|
|
942
|
+
};
|
|
943
|
+
break;
|
|
944
|
+
case 'internal':
|
|
945
|
+
const internalQuery = {
|
|
946
|
+
size: 0,
|
|
947
|
+
query: {
|
|
948
|
+
bool: {
|
|
949
|
+
must: [
|
|
950
|
+
{
|
|
951
|
+
'range': {
|
|
952
|
+
'dateString': {
|
|
953
|
+
'gte': inputData?.fromDate,
|
|
954
|
+
'lte': inputData?.toDate,
|
|
955
|
+
'format': 'yyyy-MM-dd',
|
|
956
|
+
},
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
{
|
|
961
|
+
nested: {
|
|
962
|
+
path: 'mappingInfo',
|
|
963
|
+
query: {
|
|
964
|
+
bool: {
|
|
965
|
+
must: [
|
|
966
|
+
{
|
|
967
|
+
terms: {
|
|
968
|
+
'mappingInfo.type': [ 'tagging', 'review', 'approve', 'tangoreview' ],
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
|
|
977
|
+
],
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
983
|
+
function buildInternalQueryWithStatus( baseQuery, statusValue ) {
|
|
984
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
985
|
+
// Remove any previous mappingInfo.status term
|
|
986
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
987
|
+
if ( nested ) {
|
|
988
|
+
// filter out all mappingInfo.status
|
|
989
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
990
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
991
|
+
} );
|
|
992
|
+
// add desired status
|
|
993
|
+
nested.nested.query.bool.must.push( {
|
|
994
|
+
term: {
|
|
995
|
+
'mappingInfo.status': statusValue,
|
|
996
|
+
},
|
|
997
|
+
} );
|
|
998
|
+
}
|
|
999
|
+
return q;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const buildAggInternalQuery = ( baseQuery, filters = [] ) => {
|
|
1003
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1004
|
+
|
|
1005
|
+
// locate nested section
|
|
1006
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1007
|
+
|
|
1008
|
+
if ( nested ) {
|
|
1009
|
+
// remove old status filters
|
|
1010
|
+
nested.nested.query.bool.must =
|
|
1011
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1012
|
+
|
|
1013
|
+
// add new filters
|
|
1014
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return {
|
|
1018
|
+
...q,
|
|
1019
|
+
size: 0,
|
|
1020
|
+
aggs: {
|
|
1021
|
+
avg_value: {
|
|
1022
|
+
avg: {
|
|
1023
|
+
script: {
|
|
1024
|
+
lang: 'painless',
|
|
1025
|
+
source: `
|
|
1026
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1027
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1028
|
+
try {
|
|
1029
|
+
return Double.parseDouble(v);
|
|
1030
|
+
} catch (Exception e) {
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
`,
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
};
|
|
1039
|
+
};
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
// Get OpenSearch connection
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
const baseInternalQuery = JSON.parse( JSON.stringify( internalQuery ) );
|
|
1046
|
+
|
|
1047
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1048
|
+
let totalInternalTickets = 0;
|
|
1049
|
+
|
|
1050
|
+
let allInternalQuery = JSON.parse( JSON.stringify( baseInternalQuery ) );
|
|
1051
|
+
|
|
1052
|
+
allInternalQuery.size = 0;
|
|
1053
|
+
const totalInternalResp = await getOpenSearchData( openSearch.footfallDirectory, allInternalQuery );
|
|
1054
|
+
totalInternalTickets = totalInternalResp?.body?.hits?.total?.value || 0;
|
|
1055
|
+
|
|
1056
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1057
|
+
let openInternalTickets = 0;
|
|
1058
|
+
|
|
1059
|
+
let otQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open' );
|
|
1060
|
+
otQInternal.size = 0;
|
|
1061
|
+
const openInternalResp = await getOpenSearchData( openSearch.footfallDirectory, otQInternal );
|
|
1062
|
+
openInternalTickets = openInternalResp?.body?.hits?.total?.value || 0;
|
|
1063
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1067
|
+
let openInternalInfraIssues = 0;
|
|
1068
|
+
|
|
1069
|
+
let oiQinternal = buildInternalQueryWithStatus( baseInternalQuery, 'Open Accuracy Issue' );
|
|
1070
|
+
oiQinternal.size = 0;
|
|
1071
|
+
const infraInternalResp = await getOpenSearchData( openSearch.footfallDirectory, oiQinternal );
|
|
1072
|
+
openInternalInfraIssues = infraInternalResp?.body?.hits?.total?.value || 0;
|
|
1073
|
+
|
|
1074
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1075
|
+
let inprogressIntrenal = 0;
|
|
1076
|
+
|
|
1077
|
+
let ipQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'In-Progress' );
|
|
1078
|
+
ipQInternal.size = 0;
|
|
1079
|
+
const ipInternalResp = await getOpenSearchData( openSearch.footfallDirectory, ipQInternal );
|
|
1080
|
+
inprogressIntrenal = ipInternalResp?.body?.hits?.total?.value || 0;
|
|
1081
|
+
|
|
1082
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1083
|
+
let closedInternalTickets = 0;
|
|
1084
|
+
|
|
1085
|
+
let clQInternal = buildInternalQueryWithStatus( baseInternalQuery, 'Closed' );
|
|
1086
|
+
clQInternal.size = 0;
|
|
1087
|
+
const clInternalResp = await getOpenSearchData( openSearch.footfallDirectory, clQInternal );
|
|
1088
|
+
closedInternalTickets = clInternalResp?.body?.hits?.total?.value || 0;
|
|
1089
|
+
// Average revisedPerc (for all tangoreview)
|
|
1090
|
+
let internalAverageAccuracyOverAll = 0;
|
|
1091
|
+
|
|
1092
|
+
let avgQInternal = buildAggInternalQuery( baseInternalQuery );
|
|
1093
|
+
const avgInternalResp = await getOpenSearchData( openSearch.footfallDirectory, avgQInternal );
|
|
1094
|
+
internalAverageAccuracyOverAll = avgInternalResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1095
|
+
|
|
1096
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1097
|
+
let internalTicketAccuracyAbove = 0;
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1101
|
+
let aboveQinternal = buildAggInternalQuery( baseInternalQuery, [
|
|
1102
|
+
{
|
|
1103
|
+
script: {
|
|
1104
|
+
script: {
|
|
1105
|
+
lang: 'painless',
|
|
1106
|
+
source: `
|
|
1107
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1108
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1109
|
+
`,
|
|
1110
|
+
params: { num: 85 },
|
|
1111
|
+
},
|
|
1112
|
+
},
|
|
1113
|
+
},
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
] );
|
|
1117
|
+
const aboveRespInternal = await getOpenSearchData( openSearch.footfallDirectory, aboveQinternal );
|
|
1118
|
+
internalTicketAccuracyAbove = aboveRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1119
|
+
|
|
1120
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1121
|
+
let internalTicketAccuracyBelow = 0;
|
|
1122
|
+
|
|
1123
|
+
let belowQIneranl = buildAggInternalQuery( baseInternalQuery, [
|
|
1124
|
+
{
|
|
1125
|
+
script: {
|
|
1126
|
+
script: {
|
|
1127
|
+
lang: 'painless',
|
|
1128
|
+
source: `
|
|
1129
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1130
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1131
|
+
`,
|
|
1132
|
+
params: { num: 85 },
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
1135
|
+
},
|
|
1136
|
+
|
|
1137
|
+
] );
|
|
1138
|
+
const belowRespInternal = await getOpenSearchData( openSearch.footfallDirectory, belowQIneranl );
|
|
1139
|
+
internalTicketAccuracyBelow = belowRespInternal?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1140
|
+
|
|
1141
|
+
// Final result object
|
|
1142
|
+
result = {
|
|
1143
|
+
totalTickets: totalInternalTickets,
|
|
1144
|
+
averageAccuracyOverAll: internalAverageAccuracyOverAll+'%',
|
|
1145
|
+
openTickets: openInternalTickets,
|
|
1146
|
+
openInfraIssues: openInternalInfraIssues,
|
|
1147
|
+
inprogress: inprogressIntrenal,
|
|
1148
|
+
closedTickets: closedInternalTickets,
|
|
1149
|
+
ticketAccuracyAbove: internalTicketAccuracyAbove+'%',
|
|
1150
|
+
ticketAccuracyBelow: internalTicketAccuracyBelow+'%',
|
|
1151
|
+
};
|
|
1152
|
+
break;
|
|
1153
|
+
default: '';
|
|
1154
|
+
}
|
|
1155
|
+
// result = {
|
|
1156
|
+
// totalTickets: 0,
|
|
1157
|
+
// averageAccuracyOverAll: 0,
|
|
1158
|
+
// openTickets: 0,
|
|
1159
|
+
// openInfraIssues: 0,
|
|
1160
|
+
// inprogress: 0,
|
|
1161
|
+
// closedTickets: 0,
|
|
1162
|
+
// ticketAccuracyAbove: '0%',
|
|
1163
|
+
// ticketAccuracyBelow: '0%',
|
|
1164
|
+
// };
|
|
1165
|
+
} else if ( req?.user?.userType === 'client' ) {
|
|
1166
|
+
if ( ticketsFeature && !ticketsApproveFeature ) {
|
|
1167
|
+
const storeQuery = {
|
|
1168
|
+
size: 0,
|
|
1169
|
+
query: {
|
|
1170
|
+
bool: {
|
|
1171
|
+
must: [
|
|
1172
|
+
{
|
|
1173
|
+
'range': {
|
|
1174
|
+
'dateString': {
|
|
1175
|
+
'gte': inputData?.fromDate,
|
|
1176
|
+
'lte': inputData?.toDate,
|
|
1177
|
+
'format': 'yyyy-MM-dd',
|
|
1178
|
+
},
|
|
1179
|
+
},
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
nested: {
|
|
1183
|
+
path: 'mappingInfo',
|
|
1184
|
+
query: {
|
|
1185
|
+
bool: {
|
|
1186
|
+
must: [
|
|
1187
|
+
{
|
|
1188
|
+
term: {
|
|
1189
|
+
'mappingInfo.type': 'review',
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
],
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1195
|
+
},
|
|
1196
|
+
},
|
|
1197
|
+
|
|
1198
|
+
],
|
|
1199
|
+
},
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1204
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1205
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1206
|
+
// Remove any previous mappingInfo.status term
|
|
1207
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1208
|
+
if ( nested ) {
|
|
1209
|
+
// filter out all mappingInfo.status
|
|
1210
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1211
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1212
|
+
} );
|
|
1213
|
+
// add desired status
|
|
1214
|
+
nested.nested.query.bool.must.push( {
|
|
1215
|
+
term: {
|
|
1216
|
+
'mappingInfo.status': statusValue,
|
|
1217
|
+
},
|
|
1218
|
+
} );
|
|
1219
|
+
}
|
|
1220
|
+
return q;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1224
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1225
|
+
|
|
1226
|
+
// locate nested section
|
|
1227
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1228
|
+
|
|
1229
|
+
if ( nested ) {
|
|
1230
|
+
// remove old status filters
|
|
1231
|
+
nested.nested.query.bool.must =
|
|
1232
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1233
|
+
|
|
1234
|
+
// add new filters
|
|
1235
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
return {
|
|
1239
|
+
...q,
|
|
1240
|
+
size: 0,
|
|
1241
|
+
aggs: {
|
|
1242
|
+
avg_value: {
|
|
1243
|
+
avg: {
|
|
1244
|
+
script: {
|
|
1245
|
+
lang: 'painless',
|
|
1246
|
+
source: `
|
|
1247
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1248
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1249
|
+
try {
|
|
1250
|
+
return Double.parseDouble(v);
|
|
1251
|
+
} catch (Exception e) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
`,
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
},
|
|
1259
|
+
};
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
// Get OpenSearch connection
|
|
1264
|
+
|
|
1265
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1266
|
+
|
|
1267
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1268
|
+
let totalTickets = 0;
|
|
1269
|
+
|
|
1270
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1271
|
+
|
|
1272
|
+
allQuery.size = 0;
|
|
1273
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1274
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1275
|
+
|
|
1276
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1277
|
+
let openTickets = 0;
|
|
1278
|
+
|
|
1279
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1280
|
+
otQ.size = 0;
|
|
1281
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1282
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1283
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1287
|
+
let openInfraIssues = 0;
|
|
1288
|
+
|
|
1289
|
+
let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
|
|
1290
|
+
oiQ.size = 0;
|
|
1291
|
+
const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
|
|
1292
|
+
openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
|
|
1293
|
+
|
|
1294
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1295
|
+
let inprogress = 0;
|
|
1296
|
+
|
|
1297
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1298
|
+
ipQ.size = 0;
|
|
1299
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1300
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1301
|
+
|
|
1302
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1303
|
+
let closedTickets = 0;
|
|
1304
|
+
|
|
1305
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1306
|
+
clQ.size = 0;
|
|
1307
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1308
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1309
|
+
// Average revisedPerc (for all tangoreview)
|
|
1310
|
+
let averageAccuracyOverAll = 0;
|
|
1311
|
+
|
|
1312
|
+
let avgQ = buildAggStoreQuery( baseStoreQuery );
|
|
1313
|
+
const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
|
|
1314
|
+
averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1315
|
+
|
|
1316
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1317
|
+
let ticketAccuracyAbove = 0;
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1321
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1322
|
+
{
|
|
1323
|
+
script: {
|
|
1324
|
+
script: {
|
|
1325
|
+
lang: 'painless',
|
|
1326
|
+
source: `
|
|
1327
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1328
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1329
|
+
`,
|
|
1330
|
+
params: { num: 85 },
|
|
1331
|
+
},
|
|
1332
|
+
},
|
|
1333
|
+
},
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
] );
|
|
1337
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1338
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1339
|
+
|
|
1340
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1341
|
+
let ticketAccuracyBelow = 0;
|
|
1342
|
+
|
|
1343
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1344
|
+
{
|
|
1345
|
+
script: {
|
|
1346
|
+
script: {
|
|
1347
|
+
lang: 'painless',
|
|
1348
|
+
source: `
|
|
1349
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1350
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1351
|
+
`,
|
|
1352
|
+
params: { num: 85 },
|
|
1353
|
+
},
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
|
|
1357
|
+
] );
|
|
1358
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1359
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1360
|
+
|
|
1361
|
+
// Final result object
|
|
1362
|
+
result = {
|
|
1363
|
+
totalTickets,
|
|
1364
|
+
averageAccuracyOverAll: averageAccuracyOverAll+'%',
|
|
1365
|
+
openTickets,
|
|
1366
|
+
openInfraIssues,
|
|
1367
|
+
inprogress,
|
|
1368
|
+
closedTickets,
|
|
1369
|
+
ticketAccuracyAbove: ticketAccuracyAbove+'%',
|
|
1370
|
+
ticketAccuracyBelow: ticketAccuracyBelow+'%',
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
} else if ( ticketsFeature && !ticketsApproveFeature ) {
|
|
1374
|
+
const storeQuery = {
|
|
1375
|
+
size: 0,
|
|
1376
|
+
query: {
|
|
1377
|
+
bool: {
|
|
1378
|
+
must: [
|
|
1379
|
+
{
|
|
1380
|
+
'range': {
|
|
1381
|
+
'dateString': {
|
|
1382
|
+
'gte': inputData?.fromDate,
|
|
1383
|
+
'lte': inputData?.toDate,
|
|
1384
|
+
'format': 'yyyy-MM-dd',
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
nested: {
|
|
1390
|
+
path: 'mappingInfo',
|
|
1391
|
+
query: {
|
|
1392
|
+
bool: {
|
|
1393
|
+
must: [
|
|
1394
|
+
{
|
|
1395
|
+
term: {
|
|
1396
|
+
'mappingInfo.type': inputData.permissionType === 'review'? 'review':'approve',
|
|
1397
|
+
},
|
|
1398
|
+
},
|
|
1399
|
+
],
|
|
1400
|
+
},
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
|
|
1405
|
+
],
|
|
1406
|
+
},
|
|
1407
|
+
},
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// Helper function to clone deep and replace mappingInfo.status for openTickets/closed/etc
|
|
1411
|
+
function buildStoreQueryWithStatus( baseQuery, statusValue ) {
|
|
1412
|
+
let q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1413
|
+
// Remove any previous mappingInfo.status term
|
|
1414
|
+
let nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1415
|
+
if ( nested ) {
|
|
1416
|
+
// filter out all mappingInfo.status
|
|
1417
|
+
nested.nested.query.bool.must = nested?.nested?.query?.bool?.must.filter( ( mustItem ) => {
|
|
1418
|
+
return !( mustItem.term && mustItem.term['mappingInfo.status'] );
|
|
1419
|
+
} );
|
|
1420
|
+
// add desired status
|
|
1421
|
+
nested.nested.query.bool.must.push( {
|
|
1422
|
+
term: {
|
|
1423
|
+
'mappingInfo.status': statusValue,
|
|
1424
|
+
},
|
|
1425
|
+
} );
|
|
1426
|
+
}
|
|
1427
|
+
return q;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
const buildAggStoreQuery = ( baseQuery, filters = [] ) => {
|
|
1431
|
+
const q = JSON.parse( JSON.stringify( baseQuery ) );
|
|
1432
|
+
|
|
1433
|
+
// locate nested section
|
|
1434
|
+
const nested = q.query.bool.must.find( ( m ) => m.nested );
|
|
1435
|
+
|
|
1436
|
+
if ( nested ) {
|
|
1437
|
+
// remove old status filters
|
|
1438
|
+
nested.nested.query.bool.must =
|
|
1439
|
+
nested.nested.query.bool.must.filter( ( m ) => !( m.term && m.term['mappingInfo.status'] ) );
|
|
1440
|
+
|
|
1441
|
+
// add new filters
|
|
1442
|
+
nested.nested.query.bool.must.push( ...filters );
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
return {
|
|
1446
|
+
...q,
|
|
1447
|
+
size: 0,
|
|
1448
|
+
aggs: {
|
|
1449
|
+
avg_value: {
|
|
1450
|
+
avg: {
|
|
1451
|
+
script: {
|
|
1452
|
+
lang: 'painless',
|
|
1453
|
+
source: `
|
|
1454
|
+
if (doc['revicedPerc.keyword'].size() == 0) return null;
|
|
1455
|
+
String v = doc['revicedPerc.keyword'].value.replace('%','');
|
|
1456
|
+
try {
|
|
1457
|
+
return Double.parseDouble(v);
|
|
1458
|
+
} catch (Exception e) {
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
`,
|
|
1462
|
+
},
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
},
|
|
1466
|
+
};
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
// Get OpenSearch connection
|
|
1471
|
+
|
|
1472
|
+
const baseStoreQuery = JSON.parse( JSON.stringify( storeQuery ) );
|
|
1473
|
+
|
|
1474
|
+
// Total Tickets (all tickets with mappingInfo.type == tangoreview)
|
|
1475
|
+
let totalTickets = 0;
|
|
1476
|
+
|
|
1477
|
+
let allQuery = JSON.parse( JSON.stringify( baseStoreQuery ) );
|
|
1478
|
+
|
|
1479
|
+
allQuery.size = 0;
|
|
1480
|
+
const totalResp = await getOpenSearchData( openSearch.footfallDirectory, allQuery );
|
|
1481
|
+
totalTickets = totalResp?.body?.hits?.total?.value || 0;
|
|
1482
|
+
|
|
1483
|
+
// openTickets: mappingInfo.status: 'Open'
|
|
1484
|
+
let openTickets = 0;
|
|
1485
|
+
|
|
1486
|
+
let otQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open' );
|
|
1487
|
+
otQ.size = 0;
|
|
1488
|
+
const openResp = await getOpenSearchData( openSearch.footfallDirectory, otQ );
|
|
1489
|
+
openTickets = openResp?.body?.hits?.total?.value || 0;
|
|
1490
|
+
// logger.info( { msd: '..............2', openResp } );
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
// openInfraIssues: mappingInfo.status: 'Open Accuracy Issue'
|
|
1494
|
+
let openInfraIssues = 0;
|
|
1495
|
+
|
|
1496
|
+
let oiQ = buildStoreQueryWithStatus( baseStoreQuery, 'Open Accuracy Issue' );
|
|
1497
|
+
oiQ.size = 0;
|
|
1498
|
+
const infraResp = await getOpenSearchData( openSearch.footfallDirectory, oiQ );
|
|
1499
|
+
openInfraIssues = infraResp?.body?.hits?.total?.value || 0;
|
|
1500
|
+
|
|
1501
|
+
// inprogress: mappingInfo.status: 'in-Progress'
|
|
1502
|
+
let inprogress = 0;
|
|
1503
|
+
|
|
1504
|
+
let ipQ = buildStoreQueryWithStatus( baseStoreQuery, 'In-Progress' );
|
|
1505
|
+
ipQ.size = 0;
|
|
1506
|
+
const ipResp = await getOpenSearchData( openSearch.footfallDirectory, ipQ );
|
|
1507
|
+
inprogress = ipResp?.body?.hits?.total?.value || 0;
|
|
1508
|
+
|
|
1509
|
+
// closedTickets: mappingInfo.status: 'closed'
|
|
1510
|
+
let closedTickets = 0;
|
|
1511
|
+
|
|
1512
|
+
let clQ = buildStoreQueryWithStatus( baseStoreQuery, 'Closed' );
|
|
1513
|
+
clQ.size = 0;
|
|
1514
|
+
const clResp = await getOpenSearchData( openSearch.footfallDirectory, clQ );
|
|
1515
|
+
closedTickets = clResp?.body?.hits?.total?.value || 0;
|
|
1516
|
+
// Average revisedPerc (for all tangoreview)
|
|
1517
|
+
let averageAccuracyOverAll = 0;
|
|
1518
|
+
|
|
1519
|
+
let avgQ = buildAggStoreQuery( baseStoreQuery );
|
|
1520
|
+
const avgResp = await getOpenSearchData( openSearch.footfallDirectory, avgQ );
|
|
1521
|
+
averageAccuracyOverAll = avgResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1522
|
+
|
|
1523
|
+
// ticketAccuracyAbove: avg of revicedPerc > 85%
|
|
1524
|
+
let ticketAccuracyAbove = 0;
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
// For this, add a filter on revicedPerc >= 85
|
|
1528
|
+
let aboveQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1529
|
+
{
|
|
1530
|
+
script: {
|
|
1531
|
+
script: {
|
|
1532
|
+
lang: 'painless',
|
|
1533
|
+
source: `
|
|
1534
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1535
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) >= params.num
|
|
1536
|
+
`,
|
|
1537
|
+
params: { num: 85 },
|
|
1538
|
+
},
|
|
1539
|
+
},
|
|
1540
|
+
},
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
] );
|
|
1544
|
+
const aboveResp = await getOpenSearchData( openSearch.footfallDirectory, aboveQ );
|
|
1545
|
+
ticketAccuracyAbove = aboveResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1546
|
+
|
|
1547
|
+
// ticketAccuracyBelow: avg of revicedPerc < 85%
|
|
1548
|
+
let ticketAccuracyBelow = 0;
|
|
1549
|
+
|
|
1550
|
+
let belowQ = buildAggStoreQuery( baseStoreQuery, [
|
|
1551
|
+
{
|
|
1552
|
+
script: {
|
|
1553
|
+
script: {
|
|
1554
|
+
lang: 'painless',
|
|
1555
|
+
source: `
|
|
1556
|
+
doc['revicedPerc.keyword'].size()!=0 &&
|
|
1557
|
+
Integer.parseInt(doc['revicedPerc.keyword'].value.replace('%','')) < params.num
|
|
1558
|
+
`,
|
|
1559
|
+
params: { num: 85 },
|
|
1560
|
+
},
|
|
1561
|
+
},
|
|
1562
|
+
},
|
|
1563
|
+
|
|
1564
|
+
] );
|
|
1565
|
+
const belowResp = await getOpenSearchData( openSearch.footfallDirectory, belowQ );
|
|
1566
|
+
ticketAccuracyBelow = belowResp?.body?.aggregations?.avg_value?.value?.toFixed( 2 ) || '0';
|
|
1567
|
+
|
|
1568
|
+
// Final result object
|
|
726
1569
|
result = {
|
|
727
|
-
totalTickets
|
|
728
|
-
averageAccuracyOverAll:
|
|
729
|
-
openTickets
|
|
730
|
-
openInfraIssues
|
|
731
|
-
inprogress
|
|
732
|
-
closedTickets
|
|
733
|
-
ticketAccuracyAbove: '
|
|
734
|
-
ticketAccuracyBelow: '
|
|
1570
|
+
totalTickets,
|
|
1571
|
+
averageAccuracyOverAll: averageAccuracyOverAll+'%',
|
|
1572
|
+
openTickets,
|
|
1573
|
+
openInfraIssues,
|
|
1574
|
+
inprogress,
|
|
1575
|
+
closedTickets,
|
|
1576
|
+
ticketAccuracyAbove: ticketAccuracyAbove+'%',
|
|
1577
|
+
ticketAccuracyBelow: ticketAccuracyBelow+'%',
|
|
735
1578
|
};
|
|
736
1579
|
} else {
|
|
737
1580
|
result = req.user.role === 'superadmin' ?
|
|
@@ -1317,6 +2160,87 @@ export async function ticketList( req, res ) {
|
|
|
1317
2160
|
}
|
|
1318
2161
|
}
|
|
1319
2162
|
|
|
2163
|
+
if ( inputData?.filterByStore && inputData?.filterByStore !== '' ) {
|
|
2164
|
+
let percQuery = null;
|
|
2165
|
+
const value = inputData.filterByStore;
|
|
2166
|
+
|
|
2167
|
+
// Helper function: remove trailing '%' and convert to number
|
|
2168
|
+
const percValue = ( val ) => {
|
|
2169
|
+
if ( typeof val === 'string' ) {
|
|
2170
|
+
return parseFloat( val.replace( '%', '' ).trim() );
|
|
2171
|
+
}
|
|
2172
|
+
return parseFloat( val );
|
|
2173
|
+
};
|
|
2174
|
+
|
|
2175
|
+
// Example filter values: "<90", "<=90", ">=90", "50 to 90"
|
|
2176
|
+
if ( /^<=?\d+$/.test( value ) ) {
|
|
2177
|
+
// "<90" or "<=90"
|
|
2178
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2179
|
+
const op = value.includes( '=' ) ? 'lte' : 'lt';
|
|
2180
|
+
percQuery = {
|
|
2181
|
+
script: {
|
|
2182
|
+
script: {
|
|
2183
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'lt' ? '<' : '<='} params.num`,
|
|
2184
|
+
params: { num },
|
|
2185
|
+
},
|
|
2186
|
+
},
|
|
2187
|
+
};
|
|
2188
|
+
} else if ( /^>=?\d+$/.test( value ) ) {
|
|
2189
|
+
// ">=90"
|
|
2190
|
+
const num = percValue( value.replace( /[^\d]/g, '' ) );
|
|
2191
|
+
const op = value.includes( '=' ) ? 'gte' : 'gt';
|
|
2192
|
+
percQuery = {
|
|
2193
|
+
script: {
|
|
2194
|
+
script: {
|
|
2195
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) ${op === 'gt' ? '>' : '>='} params.num`,
|
|
2196
|
+
params: { num },
|
|
2197
|
+
},
|
|
2198
|
+
},
|
|
2199
|
+
};
|
|
2200
|
+
} else if ( /^(\d+)\s*to\s*(\d+)$/.test( value ) ) {
|
|
2201
|
+
// "50 to 90"
|
|
2202
|
+
const match = value.match( /^(\d+)\s*to\s*(\d+)$/ );
|
|
2203
|
+
const from = percValue( match[1] );
|
|
2204
|
+
const to = percValue( match[2] );
|
|
2205
|
+
percQuery = {
|
|
2206
|
+
script: {
|
|
2207
|
+
script: {
|
|
2208
|
+
source:
|
|
2209
|
+
`doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) >= params.from && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) <= params.to`,
|
|
2210
|
+
params: { from, to },
|
|
2211
|
+
},
|
|
2212
|
+
},
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
// fallback: treat as exact match (e.g., "90")
|
|
2216
|
+
if ( !percQuery && /^\d+$/.test( value ) ) {
|
|
2217
|
+
percQuery = {
|
|
2218
|
+
script: {
|
|
2219
|
+
script: {
|
|
2220
|
+
source: `doc['mappingInfo.revicedPerc.keyword'].size()!=0 && Integer.parseInt(doc['mappingInfo.revicedPerc.keyword'].value.replace('%','')) == params.num`,
|
|
2221
|
+
params: { num: percValue( value ) },
|
|
2222
|
+
},
|
|
2223
|
+
},
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
if ( percQuery ) {
|
|
2228
|
+
searchQuery.query.bool.must.push( {
|
|
2229
|
+
nested: {
|
|
2230
|
+
path: 'mappingInfo',
|
|
2231
|
+
query: {
|
|
2232
|
+
bool: {
|
|
2233
|
+
must: [
|
|
2234
|
+
{ term: { 'mappingInfo.type': 'tagging' } },
|
|
2235
|
+
percQuery,
|
|
2236
|
+
],
|
|
2237
|
+
},
|
|
2238
|
+
},
|
|
2239
|
+
},
|
|
2240
|
+
} );
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
1320
2244
|
if ( inputData?.filterByReviewer && inputData?.filterByReviewer !== '' ) {
|
|
1321
2245
|
let percQuery = null;
|
|
1322
2246
|
const value = inputData.filterByReviewer;
|
|
@@ -1624,7 +2548,7 @@ export async function ticketList( req, res ) {
|
|
|
1624
2548
|
|
|
1625
2549
|
|
|
1626
2550
|
);
|
|
1627
|
-
} else if ( req?.user?.userType === 'client'
|
|
2551
|
+
} else if ( req?.user?.userType === 'client' ) {
|
|
1628
2552
|
searchQuery.query.bool.must.push(
|
|
1629
2553
|
{
|
|
1630
2554
|
term: {
|
|
@@ -2657,7 +3581,7 @@ export async function getTaggedStores( req, res ) {
|
|
|
2657
3581
|
+ (doc.containsKey('employeeCount') && !doc['employeeCount'].empty ? doc['employeeCount'].value : 0)
|
|
2658
3582
|
+ (doc.containsKey('junkCount') && !doc['junkCount'].empty ? doc['junkCount'].value : 0);
|
|
2659
3583
|
`,
|
|
2660
|
-
lang: '
|
|
3584
|
+
lang: 'scripting',
|
|
2661
3585
|
},
|
|
2662
3586
|
},
|
|
2663
3587
|
},
|
|
@@ -3067,6 +3991,7 @@ export async function reviewerList( req, res ) {
|
|
|
3067
3991
|
export async function openTicketList( req, res ) {
|
|
3068
3992
|
try {
|
|
3069
3993
|
const inputData = req.body;
|
|
3994
|
+
logger.info( { inputData } );
|
|
3070
3995
|
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
3071
3996
|
|
|
3072
3997
|
// INSERT_YOUR_CODE
|
|
@@ -86,7 +86,8 @@ export const tangoReviewAccuracyClosedTicketValid = {
|
|
|
86
86
|
|
|
87
87
|
export const ticketSummarySchema = Joi.object().keys( {
|
|
88
88
|
clientId: Joi.string().required(),
|
|
89
|
-
|
|
89
|
+
tangoType: Joi.string().valid( 'store', 'internal' ).optional(),
|
|
90
|
+
permissionType: Joi.string().valid( 'approve', 'review' ).optional(),
|
|
90
91
|
fromDate: Joi.string()
|
|
91
92
|
.pattern( /^\d{4}-\d{2}-\d{2}$/, 'YYYY-MM-DD format' )
|
|
92
93
|
.required()
|
|
@@ -159,6 +160,7 @@ export const ticketListSchema = Joi.object().keys( {
|
|
|
159
160
|
tangoType: Joi.string().valid( 'store', 'internal', '' ).optional(),
|
|
160
161
|
permissionType: Joi.string().valid( 'review', 'approve' ).optional(),
|
|
161
162
|
filterByStatus: Joi.string().optional().allow( '' ),
|
|
163
|
+
filterByStore: Joi.string().optional().allow( '' ),
|
|
162
164
|
filterByReviewer: Joi.string().optional().allow( '' ),
|
|
163
165
|
filterByApprover: Joi.string().optional().allow( '' ),
|
|
164
166
|
filterByTango: Joi.string().optional().allow( '' ),
|