tango-app-api-audit 3.4.68-beta.1 → 3.4.68-beta.3

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/README.md CHANGED
@@ -1,29 +1,29 @@
1
- # README #
2
-
3
- This README would normally document whatever steps are necessary to get your application up and running.
4
-
5
- ### What is this repository for? ###
6
-
7
- * Quick summary
8
- * Version
9
- * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
-
11
- ### How do I get set up? ###
12
-
13
- * Summary of set up
14
- * Configuration
15
- * Dependencies
16
- * Database configuration
17
- * How to run tests
18
- * Deployment instructions
19
-
20
- ### Contribution guidelines ###
21
-
22
- * Writing tests
23
- * Code review
24
- * Other guidelines
25
-
26
- ### Who do I talk to? ###
27
-
28
- * Repo owner or admin
1
+ # README #
2
+
3
+ This README would normally document whatever steps are necessary to get your application up and running.
4
+
5
+ ### What is this repository for? ###
6
+
7
+ * Quick summary
8
+ * Version
9
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
+
11
+ ### How do I get set up? ###
12
+
13
+ * Summary of set up
14
+ * Configuration
15
+ * Dependencies
16
+ * Database configuration
17
+ * How to run tests
18
+ * Deployment instructions
19
+
20
+ ### Contribution guidelines ###
21
+
22
+ * Writing tests
23
+ * Code review
24
+ * Other guidelines
25
+
26
+ ### Who do I talk to? ###
27
+
28
+ * Repo owner or admin
29
29
  * Other community or team contact
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-audit",
3
- "version": "3.4.68-beta.1",
3
+ "version": "3.4.68-beta.3",
4
4
  "description": "audit & audit metrics apis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
- import { getOpenSearchData, getOpenSearchById, logger, insertOpenSearchData, updateOpenSearchData } from 'tango-app-api-middleware';
1
+ import { getOpenSearchData, getOpenSearchById, logger, insertOpenSearchData, updateOpenSearchData, download, chunkArray } from 'tango-app-api-middleware';
2
2
  import { findOneUser } from '../service/user.service.js';
3
3
  import mongoose from 'mongoose';
4
4
  import dayjs from 'dayjs';
@@ -16,7 +16,8 @@ export async function getList( req, res ) {
16
16
  const limit = inputData.limit || 10;
17
17
  const offset = inputData.offset ? ( inputData.offset - 1 ) * limit : 0;
18
18
 
19
-
19
+ const sortBy = `${inputData?.sortBy}.keyword`;
20
+ const order = inputData?.sortOrder || -1;
20
21
  let filter= [
21
22
  {
22
23
  'range': {
@@ -28,21 +29,24 @@ export async function getList( req, res ) {
28
29
  },
29
30
 
30
31
  ];
31
- let search ={
32
- 'must': filter,
33
- };
34
32
 
35
- if ( inputData.visitorsType && inputData.visitorsType.lenght > 0 ) {
36
- filter.push( {
37
33
 
38
- 'terms': {
39
- 'visitorsType.keyword': inputData.visitorsType,
34
+ // if ( inputData.demographics && inputData.demographics.length > 0 ) {
35
+ // filter.push( {
36
+ // 'terms': {
37
+ // 'visitorsType.keyword': inputData.demographics,
38
+ // },
39
+ // } );
40
+ // }
41
+ if ( inputData.type && inputData.type!='' ) {
42
+ filter.push( {
43
+ 'term': {
44
+ 'type.keyword': inputData.type,
40
45
  },
41
46
 
42
47
  } );
43
48
  }
44
-
45
- if ( inputData.storeId && inputData.storeId.lenght > 0 ) {
49
+ if ( inputData.storeId && inputData.storeId.length > 0 ) {
46
50
  filter.push( {
47
51
 
48
52
  'terms': {
@@ -51,6 +55,9 @@ export async function getList( req, res ) {
51
55
 
52
56
  } );
53
57
  }
58
+ let search ={
59
+ 'must': filter,
60
+ };
54
61
 
55
62
  if ( inputData.searchValue && inputData.searchValue !== '' ) {
56
63
  search ={
@@ -109,14 +116,41 @@ export async function getList( req, res ) {
109
116
  'minimum_should_match': 1,
110
117
  };
111
118
  }
112
-
113
119
  let searchQuery={
114
120
  'from': offset,
115
121
  'size': limit,
116
122
  'query': {
117
123
  'bool': search,
118
124
  },
125
+ 'sort': [
126
+ { date: { order: 'desc' } },
127
+ ],
119
128
  };
129
+ if ( sortBy && sortBy !== '' ) {
130
+ searchQuery={
131
+ 'from': offset,
132
+ 'size': limit,
133
+ 'query': {
134
+ 'bool': search,
135
+ },
136
+ 'sort': [
137
+ { [sortBy]: { order: order === -1 ?'desc':'asc' } },
138
+ ],
139
+ };
140
+ }
141
+
142
+ if ( inputData.isExport==true ) {
143
+ searchQuery={
144
+ 'from': 0,
145
+ 'size': 10000,
146
+ 'query': {
147
+ 'bool': search,
148
+ },
149
+ 'sort': [
150
+ { createdAt: { order: 'desc' } },
151
+ ],
152
+ };
153
+ }
120
154
  const result = await getOpenSearchData( openSearch.EyeTestInput, searchQuery );
121
155
  const count = result?.body?.hits?.total?.value;
122
156
  if ( !count || count == 0 ) {
@@ -126,6 +160,70 @@ export async function getList( req, res ) {
126
160
  if ( !searchValue || searchValue?.length == 0 ) {
127
161
  return res.sendError( 'No data found', 204 );
128
162
  }
163
+ if ( inputData.isExport == true ) {
164
+ const chunkedMappingData = await chunkArray( searchValue, 10 );
165
+ const promises = chunkedMappingData.map( async ( chunk ) => {
166
+ const exportData = [];
167
+ chunk.forEach( async ( element ) => {
168
+ const testDuration = element?._source?.testDuration;
169
+ const minutes = Math.floor( testDuration / 60 );
170
+ const seconds = testDuration % 60;
171
+ const minutes1 =minutes> 1? `${minutes} mins` : `${minutes} min`;
172
+ const seconds1 = seconds > 1? `${minutes} secs`: seconds == 1? `${minutes} sec` : '';
173
+ const duration =`${minutes1} ${seconds1}`;
174
+ const userName = await findOneUser( { _id: element?._source?.userId }, { _id: 0, userName: 1 } );
175
+ if ( element?._source?.type == 'physical' ) {
176
+ exportData.push( {
177
+ 'Date': dayjs( element?._source?.date ).format( 'YYYY,MMM D' ),
178
+ 'Store Name': element?._source?.storeName,
179
+ 'Store ID': element?._source?.storeId || 'Un Assigned',
180
+ 'Store Name': element?._source?.storeName ||'Un Assigned',
181
+ 'Optum ID': element?._source?.optumId,
182
+ 'Queue ID': element?._source?.queueId,
183
+ 'Compliance Score': element?._source?.complianceScore || 0,
184
+ 'Steps Covered By AI': element?._source?.coveredStepsAI || 0,
185
+ 'Visitor Type': element?._source?.visitorsType,
186
+ 'Test Duration': duration || '',
187
+ 'Audit Status': element?._source?.auditStatus || '',
188
+ 'Audited By': userName?.userName || '',
189
+
190
+ } );
191
+ } else {
192
+ exportData.push( {
193
+ 'Date': dayjs( element?._source?.date ).format( 'YYYY,MMM D' ),
194
+ 'Store Name': element?._source?.storeName,
195
+ 'Store ID': element?._source?.storeId || 'Un Assigned',
196
+ 'Store Name': element?._source?.storeName ||'Un Assigned',
197
+ 'Engagement ID': element?._source?.engagementId,
198
+ 'Compliance Score': element?._source?.complianceScore || 0,
199
+ 'Steps Covered By AI': element?._source?.coveredStepsAI || 0,
200
+ 'Visitor Type': element?._source?.visitorsType,
201
+ 'Test Duration': duration || '',
202
+ 'Audit Status': element?._source?.auditStatus || '',
203
+ 'Audited By': userName.userName || '',
204
+
205
+ } );
206
+ }
207
+ } );
208
+ return exportData;
209
+ } );
210
+ const mappedArrays = await Promise.all( promises );
211
+ const result = mappedArrays.flat();
212
+ await download( result, res );
213
+ return;
214
+ } else {
215
+ for ( const [ i, item ] of searchValue.entries() ) {
216
+ const userName = await findOneUser( { _id: item?._source?.userId }, { _id: 0, userName: 1 } );
217
+ const testDuration = searchValue[i]?._source?.testDuration;
218
+ const minutes = Math.floor( testDuration / 60 );
219
+ const seconds = testDuration % 60;
220
+
221
+ searchValue[i]._source.min = minutes;
222
+ searchValue[i]._source.sec = seconds;
223
+ searchValue[i]._source.auditedBy = userName?.userName || '';
224
+ }
225
+ }
226
+
129
227
  return res.sendSuccess( { result: searchValue, count: count } );
130
228
  } catch ( error ) {
131
229
  const err = error.message || 'Internal Server Error';
@@ -150,13 +248,13 @@ export async function viewFile( req, res ) {
150
248
 
151
249
  let searchFilter =[
152
250
  {
153
- 'terms': {
251
+ 'term': {
154
252
  'queueId.keyword': getData?.queueId,
155
253
  },
156
254
 
157
255
  },
158
256
  {
159
- 'terms': {
257
+ 'term': {
160
258
  'type.keyword': getData?.type,
161
259
  },
162
260
 
@@ -165,13 +263,13 @@ export async function viewFile( req, res ) {
165
263
  if ( getData?.type == 'remote' ) {
166
264
  searchFilter =[
167
265
  {
168
- 'terms': {
266
+ 'term': {
169
267
  'engagementId.keyword': getData?.engagementId,
170
268
  },
171
269
 
172
270
  },
173
271
  {
174
- 'terms': {
272
+ 'term': {
175
273
  'type.keyword': getData?.type,
176
274
  },
177
275
 
@@ -190,21 +288,25 @@ export async function viewFile( req, res ) {
190
288
  { createdAt: { order: 'desc' } },
191
289
  ],
192
290
  };
291
+
193
292
  const searchRes= await getOpenSearchData( openSearch.EyeTestOutput, searchQuery );
194
- const temp = searchRes?.body?.hits?.hits?.lenght > 0? searchRes?.body?.hits?.hits[0] : [];
195
- const getUserName = await findOneUser( { _id: new mongoose.Types.ObjectId( temp?.userId ) }, { userName: 1 } );
293
+ const temp = searchRes?.body?.hits?.hits?.length > 0? searchRes?.body?.hits?.hits[0] : [];
294
+
295
+ const getUserName = await findOneUser( { _id: new mongoose.Types.ObjectId( temp?._source?.userId ) }, { userName: 1 } );
196
296
 
197
297
  const searchData = temp?
198
298
  getData['lastAuditedDetails']= {
199
299
  'auditedBy': getUserName?.userName || '',
200
- 'completionTime': temp?.startTime? new Date( temp?.startTime ).toLocaleTimeString( 'en-IN', { hour12: false } ): '',
300
+ 'completionTime': temp?._source?.startTime? new Date( temp?._source?.startTime ).toLocaleTimeString( 'en-IN', { hour12: false } ): '',
201
301
  'timeZone': temp?.timeZone || '',
202
302
  } : null;
203
303
  getData['complianceScore'] = getData.complianceScore || getData?.totalSteps>0 ? Math.round( ( getData?.coveredStepsAI/ getData?.totalSteps )*100 ): 0;
204
304
  getData['trustScore'] = getData?.trustScore || 0;
205
305
  getData['testDuration'] = Math.round( getData?.testDuration/60 ) || 0;
206
306
  getData['filePath'] = `${url[getData?.type]}${getData?.filePath}`;
307
+ getData['auditType'] = getData.auditType || 'Audit';
207
308
  const response = getData;
309
+
208
310
  switch ( getData?.auditStatus ) {
209
311
  case 'Yet-to-Audit':
210
312
  isEnable=true;
@@ -216,9 +318,9 @@ export async function viewFile( req, res ) {
216
318
  if ( searchData.userId !== req.user._id ) {
217
319
  isEnable=false;
218
320
  }
219
- return res.sendSuccess( { result: result } );
220
321
  break;
221
322
  case 'Audited':
323
+ response['steps']= temp._source?.steps;
222
324
  case 'Re-Audited':
223
325
  isEnable=true;
224
326
  }
@@ -226,7 +328,7 @@ export async function viewFile( req, res ) {
226
328
  return res.sendSuccess( { result: response, isEnable: isEnable } );
227
329
  } catch ( error ) {
228
330
  const err = error.message || 'Internal Server Error';
229
- logger.error( { message: error, data: req.body, function: 'eyetestAudit-controller-viewFile' } );
331
+ logger.error( { message: error, data: req.params, function: 'eyetestAudit-controller-viewFile' } );
230
332
  return res.sendError( err, 500 );
231
333
  }
232
334
  }
@@ -321,6 +423,7 @@ export async function getFile( req, res ) {
321
423
  'startTime': dayjs().tz( 'Asia/Kolkata' ),
322
424
  'createdAt': dayjs().tz( 'Asia/Kolkata' ),
323
425
  'updatedAt': dayjs().tz( 'Asia/Kolkata' ),
426
+ 'auditType': 'Audit',
324
427
 
325
428
  };
326
429
  record?.steps?.map( ( item, i ) => {
@@ -329,7 +432,7 @@ export async function getFile( req, res ) {
329
432
  if ( !searchData ) {
330
433
  const data1 = await insertOpenSearchData( openSearch.EyeTestOutput, record );
331
434
  logger.info( { data1: data1 } );
332
- await updateOpenSearchData( openSearch.EyeTestInput, inputData.id, { doc: { auditStatus: 'In-Progress', userId: req?.user?._id } } );
435
+ await updateOpenSearchData( openSearch.EyeTestInput, inputData.id, { doc: { auditStatus: 'In-Progress', auditType: 'Audit', userId: req?.user?._id } } );
333
436
  }
334
437
 
335
438
  break;
@@ -370,13 +473,14 @@ export async function getFile( req, res ) {
370
473
  'totalSteps': getData?.totalSteps,
371
474
  'filePath': `${url[getData?.type]}${getData?.filePath}`,
372
475
  'steps': getData?.steps,
476
+ 'auditType': 'Re-Audit',
373
477
  'startTime': dayjs().tz( 'Asia/Kolkata' ),
374
478
  'createdAt': dayjs().tz( 'Asia/Kolkata' ),
375
479
  'updatedAt': dayjs().tz( 'Asia/Kolkata' ),
376
480
 
377
481
  };
378
482
  await insertOpenSearchData( openSearch.EyeTestOutput, newRecord );
379
- await updateOpenSearchData( openSearch.EyeTestInput, inputData.id, { doc: { auditStatus: 'In-Progress' } } );
483
+ await updateOpenSearchData( openSearch.EyeTestInput, inputData.id, { doc: { auditStatus: 'In-Progress', auditType: 'Re-Audit' } } );
380
484
 
381
485
  break;
382
486
  default:
@@ -390,6 +494,101 @@ export async function getFile( req, res ) {
390
494
  return res.sendError( err, 500 );
391
495
  }
392
496
  }
497
+ export async function save( req, res ) {
498
+ try {
499
+ const inputData = req.body;
500
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
501
+
502
+ let searchFilter =[
503
+ {
504
+ 'term': {
505
+ 'queueId.keyword': inputData?.fileId,
506
+ },
507
+
508
+ },
509
+ {
510
+ 'term': {
511
+ 'type.keyword': inputData?.type,
512
+ },
513
+
514
+ },
515
+
516
+ {
517
+ 'term': {
518
+ 'auditStatus.keyword': 'In-Progress',
519
+ },
520
+ },
521
+ ];
522
+ if ( inputData?.type == 'remote' ) {
523
+ searchFilter =[
524
+ {
525
+ 'term': {
526
+ 'engagementId.keyword': inputData?.fileId,
527
+ },
528
+
529
+ },
530
+ {
531
+ 'term': {
532
+ 'type.keyword': inputData?.type,
533
+ },
534
+
535
+ },
536
+ {
537
+ 'term': {
538
+ 'userId.keyword': req?.user?._id,
539
+ },
540
+ },
541
+ {
542
+ 'term': {
543
+ 'auditStatus.keyword': 'In-Progress',
544
+ },
545
+ },
546
+ ];
547
+ }
548
+
549
+ let searchQuery={
550
+ 'size': 1,
551
+ 'query': {
552
+ 'bool': {
553
+ 'must': searchFilter,
554
+ },
555
+ },
556
+ };
557
+ const getOutput = await getOpenSearchData( openSearch.EyeTestOutput, searchQuery );
558
+ logger.info( { getOutput: getOutput } );
559
+ const output = getOutput?.body?.hits?.hits?.length > 0 ? getOutput?.body?.hits?.hits[0] : null;
560
+ if ( !output ) {
561
+ return res.sendError( 'No saved records found', 400 );
562
+ }
563
+ const record ={
564
+ steps: inputData?.steps,
565
+ auditStatus: inputData?.auditStatus,
566
+ auditedSteps: inputData?.auditedSteps,
567
+ overRides: inputData?.overRides,
568
+ trustScore: Math.round( 100-( ( inputData?.overRides / inputData?.steps?.length ) * 100 ) ),
569
+ spokenLanguage: inputData?.spokenLanguage,
570
+ endTime: dayjs().tz( 'Asia/Kolkata' ),
571
+
572
+ };
573
+ const inputrecord ={
574
+
575
+ auditStatus: inputData?.auditStatus,
576
+ auditedSteps: inputData?.auditedSteps,
577
+ overRides: inputData?.overRides,
578
+ trustScore: Math.round( 100-( ( inputData?.overRides / inputData?.steps?.length ) * 100 ) ),
579
+ spokenLanguage: inputData?.spokenLanguage,
580
+ endTime: dayjs().tz( 'Asia/Kolkata' ),
581
+
582
+ };
583
+ await updateOpenSearchData( openSearch.EyeTestOutput, output?._id, { doc: record } );
584
+ await updateOpenSearchData( openSearch.EyeTestInput, inputData.id, { doc: inputrecord } );
585
+ return res.sendSuccess( 'The file has been submited successfully' );
586
+ } catch ( error ) {
587
+ const err = error.message || 'Internal Server Error';
588
+ logger.error( { message: error, data: req.params, function: 'eyetestAudit-controller-save' } );
589
+ return res.sendError( err, 500 );
590
+ }
591
+ }
393
592
 
394
593
  export async function getFileHistory( req, res ) {
395
594
  try {
@@ -460,7 +659,9 @@ export async function userAuditedData( req, res ) {
460
659
  try {
461
660
  const parsedOpenSearch = JSON.parse( process.env.OPENSEARCH );
462
661
  const inputData = req.params;
662
+
463
663
  const resposnse = await getOpenSearchById( parsedOpenSearch.EyeTestOutput, inputData.id );
664
+
464
665
  if ( resposnse.body&&resposnse.body._source ) {
465
666
  return res.sendSuccess( { result: resposnse.body._source.steps } );
466
667
  } else {
@@ -1,5 +1,5 @@
1
1
  import j2s from 'joi-to-swagger';
2
- import { getListSchema, getFileSchema, getFileHistorySchema, userAuditedDataSchema, viewFileSchema } from '../dtos/eyeTestAudit.dtos.js';
2
+ import { getListSchema, getFileSchema, getFileHistorySchema, userAuditedDataSchema, viewFileSchema, saveSchema } from '../dtos/eyeTestAudit.dtos.js';
3
3
 
4
4
  export const eyeTestAuditDocs = {
5
5
 
@@ -71,6 +71,28 @@ export const eyeTestAuditDocs = {
71
71
  },
72
72
  },
73
73
 
74
+ '/v3/eye-test-audit/save': {
75
+ post: {
76
+ tags: [ 'Eye Test Audit' ],
77
+ description: 'submit eytest audit',
78
+ operationId: 'save',
79
+ requestBody: {
80
+ content: {
81
+ 'application/json': {
82
+ schema: j2s( saveSchema ).swagger,
83
+ },
84
+ },
85
+ },
86
+ responses: {
87
+ 200: { description: 'Successful' },
88
+ 401: { description: 'Unauthorized User' },
89
+ 422: { description: 'Field Error' },
90
+ 500: { description: 'Server Error' },
91
+ 204: { description: 'Not Found' },
92
+ },
93
+ },
94
+ },
95
+
74
96
  '/v3/eye-test-audit/get-file-history/{id}': {
75
97
  get: {
76
98
  tags: [ 'Eye Test Audit' ],
@@ -11,6 +11,8 @@ export const getListSchema = joi.object(
11
11
  offset: joi.number().optional(),
12
12
  searchValue: joi.string().optional().allow( '' ),
13
13
  isExport: joi.boolean().optional(),
14
+ sortBy: joi.string().optional(),
15
+ sortOrder: joi.number().optional(),
14
16
  },
15
17
  );
16
18
 
@@ -35,6 +37,22 @@ export const getFileValid = {
35
37
  params: getFileSchema,
36
38
  };
37
39
 
40
+ export const saveSchema = joi.object( {
41
+ type: joi.string().required(),
42
+ fileId: joi.string().required(),
43
+ id: joi.string().required(),
44
+ steps: joi.array().required(),
45
+ auditedSteps: joi.number().required(),
46
+ auditStatus: joi.string().required(),
47
+ overRides: joi.number().required(),
48
+ spokenLanguage: joi.string().optional(),
49
+ } );
50
+
51
+ export const saveValid = {
52
+ body: saveSchema,
53
+ };
54
+
55
+
38
56
  export const getFileHistorySchema = joi.object( {
39
57
  id: joi.string().required(),
40
58
  type: joi.string().required(),
@@ -1,15 +1,15 @@
1
1
  import { Router } from 'express';
2
2
  import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
3
3
  import { accessVerification } from 'tango-app-api-middleware';
4
- import { getFileHistoryValid, getFileValid, getListValid, userAuditedDataValid, viewFileValid } from '../dtos/eyeTestAudit.dtos.js';
5
- import { getFile, getFileHistory, getList, userAuditedData, viewFile } from '../controllers/eyeTestAudit.controllers.js';
4
+ import { getFileHistoryValid, getFileValid, getListValid, saveValid, userAuditedDataValid, viewFileValid } from '../dtos/eyeTestAudit.dtos.js';
5
+ import { getFile, getFileHistory, getList, save, userAuditedData, viewFile } from '../controllers/eyeTestAudit.controllers.js';
6
6
 
7
7
  export const eyeTestAuditRouter = Router();
8
8
 
9
9
  eyeTestAuditRouter.post( '/get-list', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getListValid ), getList );
10
10
  eyeTestAuditRouter.get( '/view-file/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( viewFileValid ), viewFile );
11
11
  eyeTestAuditRouter.get( '/get-file/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getFileValid ), getFile );
12
- eyeTestAuditRouter.post( '/save', isAllowedSessionHandler );
12
+ eyeTestAuditRouter.post( '/save', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( saveValid ), save );
13
13
  eyeTestAuditRouter.get( '/get-file-history/:id/:type', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( getFileHistoryValid ), getFileHistory );
14
14
  eyeTestAuditRouter.get( '/user-audited-data/:id', isAllowedSessionHandler, accessVerification( { userType: [ 'tango', 'client' ] } ), validate( userAuditedDataValid ), userAuditedData );
15
15