tango-app-api-trax 3.7.75 → 3.7.76

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.
@@ -26,6 +26,7 @@ import * as teamsServices from '../services/teams.service.js';
26
26
  import * as runAIFeatureServices from '../services/runAIFeatures.services.js';
27
27
  import * as runAIRequestServices from '../services/runAIRequest.services.js';
28
28
  import * as processedTaskService from '../services/processedTaskList.service.js';
29
+ import ExcelJS from 'exceljs';
29
30
 
30
31
 
31
32
  export const checklist = async ( req, res ) => {
@@ -183,7 +184,7 @@ export const create = async ( req, res ) => {
183
184
  checkNumber = result.length > 0 ? result[0].maxCheckListNumber + 1 : 0;
184
185
 
185
186
  let runAIQuestionCount = 0;
186
-
187
+ let complianceCount = 0;
187
188
  inputBody.sections.forEach( async ( element ) => {
188
189
  if ( !element?.questions?.length && inputBody.submitType == 'configure' ) {
189
190
  return res.sendError( { message: 'Question is Required' }, 400 );
@@ -191,6 +192,9 @@ export const create = async ( req, res ) => {
191
192
  if ( element?.questions?.length ) {
192
193
  questionCount = questionCount + element?.questions?.length;
193
194
  }
195
+ element.questions.forEach( ( question ) => {
196
+ complianceCount += Math.max( ...question.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatched ?? 0 ) ) );
197
+ } );
194
198
  let runAiQuestions = element?.questions.filter( ( qn ) => qn.runAI );
195
199
  runAIQuestionCount += runAiQuestions.length;
196
200
  } );
@@ -206,6 +210,7 @@ export const create = async ( req, res ) => {
206
210
  client_id: req.body?.clientId,
207
211
  owner: req.user.userType == 'client' ? [ { name: req.user.userName, value: req.user.email } ] : [],
208
212
  runAIQuestionCount: runAIQuestionCount,
213
+ complianceCount: complianceCount,
209
214
  // configStartDate:new Date(),
210
215
  // configEndDate:new Date(),
211
216
  };
@@ -982,6 +987,7 @@ export const update = async ( req, res ) => {
982
987
 
983
988
  let getExistQuestions = await questionService.findSort( { checkListId: req.params.checklistId, client_id: req.body.clientId }, {}, { sectionNumber: 1 } );
984
989
  let runAIQuestionCount = 0;
990
+ let complianceCount = 0;
985
991
  inputBody.sections.forEach( async ( element ) => {
986
992
  if ( !element.questions.length && inputBody.submitType == 'configure' ) {
987
993
  return res.sendError( { message: 'Question is Required' }, 400 );
@@ -990,6 +996,9 @@ export const update = async ( req, res ) => {
990
996
  if ( element.questions.length ) {
991
997
  questionCount = questionCount + element.questions.length;
992
998
  }
999
+ element.questions.forEach( ( question ) => {
1000
+ complianceCount += Math.max( ...question.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatched ?? 0 ) ) );
1001
+ } );
993
1002
  let runAiQuestions = element?.questions.filter( ( qn ) => qn.runAI );
994
1003
  runAIQuestionCount += runAiQuestions.length;
995
1004
  } );
@@ -1000,6 +1009,7 @@ export const update = async ( req, res ) => {
1000
1009
  checkListDescription: inputBody.checklistDescription,
1001
1010
  questionCount: questionCount,
1002
1011
  runAIQuestionCount: runAIQuestionCount,
1012
+ complianceCount: complianceCount,
1003
1013
  };
1004
1014
 
1005
1015
  await checklistService.updateOne( { _id: req.params.checklistId }, params );
@@ -3930,6 +3940,7 @@ async function insertPCBulkV4( getCLconfig, checklistId, currentdate, updatedche
3930
3940
  element4.rawImageUpload = getCLconfig?.rawImageUpload || false;
3931
3941
  element4.rawVideoUpload = getCLconfig?.rawVideoUpload || false;
3932
3942
  element4.videoUploadTimeLimit = getCLconfig?.videoUploadTimeLimit || 0;
3943
+ element4.complianceCount = getCLconfig?.complianceCount || 0;
3933
3944
  assignUserList.push( { ...element4 } );
3934
3945
  }
3935
3946
  } ) );
@@ -5075,3 +5086,71 @@ export async function updateOSProcessedData( req, res ) {
5075
5086
  return res.sendError( error, 500 );
5076
5087
  }
5077
5088
  }
5089
+
5090
+ export async function downloadQuestionTemplate( req, res ) {
5091
+ try {
5092
+ let questionDetails = await questionService.find( { checkListId: req.body.checklistId } );
5093
+ let answerType = {
5094
+ 'descriptive': 'Descriptive Answer',
5095
+ 'yes/no': 'A/B Question',
5096
+ 'multiplechoicesingle': 'Multiple Choice Single',
5097
+ 'multiplechoicemultiple': 'Multiple Choice Multiple',
5098
+ 'descriptiveImage': 'Capture Image with Description',
5099
+ 'image': 'Capture Image as answer',
5100
+ 'video': 'Capture video as answer',
5101
+ 'multipleImage': 'Capture Multiple Image',
5102
+ 'date': 'Date',
5103
+ 'linearscale': 'Linear Scale',
5104
+ 'image/video': 'Capture Image/Video as Answer',
5105
+ 'time': 'Time',
5106
+ 'dropdown': 'Dropdown',
5107
+ };
5108
+ const workbook = new ExcelJS.Workbook();
5109
+ const sheet = workbook.addWorksheet( 'Fixture Library' );
5110
+
5111
+ sheet.getRow( 1 ).values = [ 'Section Name', 'Question', 'Answer Type', 'Answer Options' ];
5112
+
5113
+ let rowStart = 2;
5114
+
5115
+ questionDetails.forEach( ( section ) => {
5116
+ section.question.forEach( ( qn ) => {
5117
+ sheet.getRow( rowStart ).values = [ section.section, qn.qname, answerType[`${qn.answerType}`], [ 'multiplechoicesingle', 'multiplechoicemultiple', 'dropdown' ].includes( qn.answerType ) ? qn.answers.map( ( ans ) => ans.answer )?.toString() : '' ];
5118
+ rowStart += 1;
5119
+ } );
5120
+ } );
5121
+
5122
+
5123
+ const maxRows = 500000;
5124
+
5125
+ let dropDownRange = [ { key: `C2:C${maxRows}`, optionList: [ '"Descriptive Answer,A/B Question,Multiple Choice Single,Multiple Choice Multiple,Capture Image with Description,Capture Image as answer,Capture video as answer,Capture Multiple Image,Date,Linear Scale,Capture Image/Video as Answer,Time,Dropdown,"' ] } ];
5126
+
5127
+ dropDownRange.forEach( ( ele ) => {
5128
+ sheet.dataValidations.add( ele.key, {
5129
+ type: 'list',
5130
+ allowBlank: true,
5131
+ formulae: ele.optionList,
5132
+ showErrorMessage: true,
5133
+ errorTitle: 'Invalid Choice',
5134
+ error: 'Please select from the dropdown list.',
5135
+ } );
5136
+ } );
5137
+
5138
+ sheet.columns.forEach( ( column ) => {
5139
+ let maxLength = 10;
5140
+ column.eachCell( { includeEmpty: true }, ( cell ) => {
5141
+ const cellValue = cell.value ? cell.value.toString() : '';
5142
+ if ( cellValue.length > maxLength ) {
5143
+ maxLength = cellValue.length;
5144
+ }
5145
+ } );
5146
+ column.width = maxLength + 2;
5147
+ } );
5148
+ const buffer = await workbook.xlsx.writeBuffer();
5149
+ res.setHeader( 'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' );
5150
+ res.setHeader( 'Content-Disposition', 'attachment; filename="Checklist Question.xlsx"' );
5151
+ return res.send( buffer );
5152
+ } catch ( e ) {
5153
+ logger.error( { functionName: 'downloadQuestionTemplate', error: e } );
5154
+ return res.sendError( e, 500 );
5155
+ }
5156
+ }
@@ -276,6 +276,28 @@ export const checklistPerformance = async ( req, res ) => {
276
276
  toDate = new Date( toDate.getTime() - userTimezoneOffset );
277
277
  toDate.setUTCHours( 23, 59, 59, 59 );
278
278
  let result = {};
279
+ let checklistIdList = [];
280
+
281
+ let limit = parseInt( requestData?.limit ) || 10;
282
+ let skip = limit * ( requestData?.offset ) || 0;
283
+
284
+ const detectionPayload = {
285
+ 'fromDate': requestData.fromDate,
286
+ 'toDate': requestData.toDate,
287
+ 'clientId': requestData.clientId,
288
+ 'sortColumnName': requestData.sortColumnName,
289
+ 'sortBy': requestData.sortBy,
290
+ 'storeId': requestData.storeId,
291
+ };
292
+
293
+
294
+ let complianceURL = 'https://h5gwudrwylz65s4vb6h2evwxeq0kkjog.lambda-url.ap-south-1.on.aws/';
295
+ const complianceData = await LamdaServiceCall( complianceURL, detectionPayload );
296
+ if ( complianceData?.data?.length && requestData?.sortColumnName == 'questionCompliance' ) {
297
+ const end = skip + requestData?.limit;
298
+ checklistIdList = complianceData.data.slice( skip, end )?.map( ( ele ) => ele?.sourceCheckList_id );
299
+ }
300
+
279
301
 
280
302
  // Get User Based Checklist //
281
303
  // let loginUser = { clientId: requestData.clientId, role: req.user.role, userType: req.user.userType, userEmail: req.user.email };
@@ -292,6 +314,10 @@ export const checklistPerformance = async ( req, res ) => {
292
314
  { $or: [ { store_id: { $in: requestData.storeId } }, { store_id: { $eq: '' }, userEmail: { $in: requestData.userEmailes } } ] },
293
315
  );
294
316
 
317
+ if ( requestData?.sortColumnName == 'questionCompliance' ) {
318
+ findAndQuery.push( { sourceCheckList_id: { $in: checklistIdList } } );
319
+ }
320
+
295
321
  findQuery.push( { $match: { $and: findAndQuery } } );
296
322
 
297
323
  if ( requestData.searchValue && requestData.searchValue != '' ) {
@@ -409,9 +435,6 @@ export const checklistPerformance = async ( req, res ) => {
409
435
  findQuery.push( { $sort: { ['submittedChecklist']: -1 } } );
410
436
  }
411
437
 
412
- let limit = parseInt( requestData?.limit ) || 10;
413
- let skip = limit * ( requestData?.offset ) || 0;
414
-
415
438
  findQuery.push( {
416
439
  $facet: {
417
440
  data: [
@@ -427,6 +450,14 @@ export const checklistPerformance = async ( req, res ) => {
427
450
  return res.sendError( 'no data found', 204 );
428
451
  }
429
452
 
453
+ getChecklistPerformanceData?.[0]?.data.forEach( ( ele ) => {
454
+ let findCompliance;
455
+ if ( complianceData?.data?.length ) {
456
+ findCompliance = complianceData?.data?.find( ( data ) => data.sourceCheckList_id == ele?.sourceCheckList_id );
457
+ }
458
+ ele['questionComplianceRate'] = findCompliance?.compliancePercentage ?? 0;
459
+ } );
460
+
430
461
  if ( requestData.export ) {
431
462
  const exportdata = [];
432
463
  getChecklistPerformanceData[0].data.forEach( ( element ) => {
@@ -4303,3 +4334,27 @@ function escapeRegex( text ) {
4303
4334
  // return [ ];
4304
4335
  // }
4305
4336
  // }
4337
+
4338
+ async function LamdaServiceCall( url, data ) {
4339
+ try {
4340
+ const requestOptions = {
4341
+ method: 'POST',
4342
+ headers: {
4343
+ 'Content-Type': 'application/json',
4344
+ },
4345
+ body: JSON.stringify( data ),
4346
+ };
4347
+ console.log( data );
4348
+ const response = await fetch( url, requestOptions );
4349
+ if ( !response.ok ) {
4350
+ throw new Error( `Response status: ${response.status}` );
4351
+ return false;
4352
+ }
4353
+ const json = await response.json();
4354
+ return json;
4355
+ } catch ( error ) {
4356
+ console.log( error );
4357
+ logger.error( { error: error, message: data, function: 'LamdaServiceCall' } );
4358
+ return false;
4359
+ }
4360
+ }
@@ -0,0 +1,249 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+
6
+ <meta charset="utf-8">
7
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
8
+ <title>Trial Intiate Email</title>
9
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ <style type="text/css">
11
+ @media screen {
12
+ @font-face {
13
+ font-family: 'Inter';
14
+ font-style: normal;
15
+ font-weight: 400;
16
+ font-display: swap;
17
+ src: local("Inter"), local("Inter-Regular"), url(https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiI2B.woff2) format('woff2');
18
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
19
+ }
20
+ }
21
+
22
+ body {
23
+ font-family: "Inter", sans-serif !important;
24
+ }
25
+
26
+ body,
27
+ table,
28
+ td,
29
+ a {
30
+ -ms-text-size-adjust: 100%;
31
+ -webkit-text-size-adjust: 100%;
32
+ }
33
+
34
+ table,
35
+ td {
36
+ mso-table-rspace: 0pt;
37
+ mso-table-lspace: 0pt;
38
+ }
39
+
40
+ img {
41
+ -ms-interpolation-mode: bicubic;
42
+ }
43
+
44
+ a[x-apple-data-detectors] {
45
+ font-family: "inherit" !important;
46
+ font-size: inherit !important;
47
+ font-weight: inherit !important;
48
+ line-height: inherit !important;
49
+ color: inherit !important;
50
+ text-decoration: none !important;
51
+ }
52
+
53
+
54
+ div[style*="margin: 16px 0;"
55
+
56
+ ] {
57
+ margin: 0 !important;
58
+ }
59
+
60
+ body {
61
+ width: 100% !important;
62
+ height: 100% !important;
63
+ padding: 0 !important;
64
+ margin: 0 !important;
65
+ }
66
+
67
+
68
+ table {
69
+ border-collapse: collapse !important;
70
+ }
71
+
72
+ a {
73
+ color: #1a82e2;
74
+ }
75
+
76
+ img {
77
+ height: auto;
78
+ line-height: 100%;
79
+ text-decoration: none;
80
+ border: 0;
81
+ outline: none;
82
+ }
83
+
84
+ .flagText {
85
+ /* font-family: 'Inter'; */
86
+ /* color: #667085 !important; */
87
+ font-size: 16px;
88
+ font-weight: 600;
89
+ line-height: 24px;
90
+ text-align: left;
91
+
92
+ }
93
+ </style>
94
+
95
+ </head>
96
+
97
+ <body style="background-color: #dbe5ea;">
98
+
99
+ <div class="preheader"
100
+ style="display: none; max-width: 0; max-height: 0; overflow: hidden; font-size: 1px; line-height: 1px; color: #fff; opacity: 0;">
101
+ Email Summary (Hidden)
102
+ </div>
103
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding-left:10px;padding-right:10px">
104
+ <tr>
105
+ <td bgcolor="#dbe5ea" style="padding:32px 10px 0 10px">
106
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;" align="center">
107
+ <tr>
108
+ <td class="o_bg-white o_px-md o_py-md o_sans o_text"
109
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 24px;background-color: #ffffff;padding-left: 18px;padding-right: 24px;padding-top: 24px;padding-bottom: 24px;">
110
+ <p style="margin-top: 0px;margin-bottom: 0px;"><a class="o_text-white"
111
+ href="https://tangoeye.ai/"
112
+ style="text-decoration: none;outline: none;color: #ffffff;"><img
113
+ src="https://devtangoretail-api.tangoeye.ai/logo.png" width="200" height="100"
114
+ alt="SimpleApp"
115
+ style="-ms-interpolation-mode: bicubic;vertical-align: middle;border: 0;line-height: 100%;height: auto;outline: none;text-decoration: none;"></a>
116
+ </p>
117
+ </td>
118
+ </tr>
119
+ <tr>
120
+ <td align="left" bgcolor="#ffffff"
121
+ style="padding-left: 30px;padding-right: 24px; font-size: 14px; line-height: 24px;">
122
+ <p class="o_heading o_mb-xxs"
123
+ style="width: 624px;height: 0px;border: 1px solid #CBD5E1;flex: none;order: 1;flex-grow: 0;">
124
+ </p>
125
+ </td>
126
+ </tr>
127
+ <tr>
128
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
129
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
130
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
131
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
132
+ <span class="o_heading o_text-dark o_mb-xxs"
133
+ style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
134
+ Hi,<br />
135
+ Flag has been detected based on the response submitted in a recently completed
136
+ checklist for Store {{data.storeName}}. Review
137
+ the flagged item and take the necessary action from the dashboard.
138
+ </span>
139
+ </div>
140
+ </td>
141
+ </tr>
142
+ <tr>
143
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
144
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom: 10px;">
145
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
146
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 16px;line-height: 28px;color: #82899a;">
147
+ <span class="o_heading o_text-dark o_mb-xxs"
148
+ style="font-weight: 400;margin-top: 0px;margin-bottom: 4px;color: #121A26;line-height: 140%;">
149
+ Details of the checklist are as follows:
150
+ </span>
151
+ </div>
152
+ </td>
153
+ </tr>
154
+ </table>
155
+ </td>
156
+ </tr>
157
+ <tr>
158
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
159
+ <table align="center" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0" width="100%"
160
+ style="max-width: 680px;">
161
+ <tr bgcolor="#ffffff" style="border:none;margin-top:0px;">
162
+ <td class="flagText" style="padding-left:30px; line-height: 24px;color:#000000">Store Name :
163
+ </td>
164
+ <td></td>
165
+ <td></td>
166
+ <td class="flagText">{{data.storeName}}</td>
167
+ </tr>
168
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
169
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">Checklist Name :</td>
170
+ <td></td>
171
+ <td></td>
172
+ <td class="flagText">{{data.checklistName}}</td>
173
+ </tr>
174
+
175
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
176
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">No of Flags :</td>
177
+ <td></td>
178
+ <td></td>
179
+ <td class="flagText">{{data.flagCount}}</td>
180
+ </tr>
181
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
182
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">Submitted By :</td>
183
+ <td></td>
184
+ <td></td>
185
+ <td class="flagText">{{data.submittedBy}}</td>
186
+ </tr>
187
+ <tr bgcolor="#ffffff" style="border:none;margin-top:3px;">
188
+ <td class="flagText" style="padding-left:30px; line-height: 24px;">Date & Time :</td>
189
+ <td></td>
190
+ <td></td>
191
+ <td class="flagText">{{data.time}}</td>
192
+ </tr>
193
+ </table>
194
+ </td>
195
+ </tr>
196
+ <tr>
197
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 0 10px">
198
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
199
+ <tr>
200
+ <td bgcolor="#ffffff" style="padding: 20px;padding-top:15px;padding-left:30px;">
201
+ <table border="0" cellpadding="0" cellspacing="0">
202
+ <tr>
203
+ <td align="center" bgcolor="#00A3FF" style="border-radius: 6px;height:50px;">
204
+ <a href="{{data.domain}}" target="_blank"
205
+ style="display: inline-block; padding: 16px 36px; font-size: 16px; color: #ffffff; text-decoration: none; border-radius: 6px;">View
206
+ Flags
207
+ </a>
208
+ </td>
209
+ </tr>
210
+ </table>
211
+ </td>
212
+ </tr>
213
+ </table>
214
+ </td>
215
+ </tr>
216
+ <tr>
217
+ <td align="center" bgcolor="#dbe5ea" style="padding:0 10px 32px 10px;">
218
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 680px;">
219
+ <tr>
220
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
221
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-top:5px">
222
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
223
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
224
+ <p>
225
+ If you have any questions or need assistance, please reach out to us at
226
+ support@tangotech.co.in.
227
+ </p>
228
+ </div>
229
+ </td>
230
+ </tr>
231
+ <tr>
232
+ <td class="o_bg-white o_px-md o_py-xl o_xs-py-md"
233
+ style="background-color: #ffffff;padding-left: 30px;padding-right: 24px;padding-bottom:15px">
234
+ <div class="o_col-6s o_sans o_text-md o_text-light o_center"
235
+ style="margin-top: 0px;margin-bottom: 0px;font-size: 12px;color: #202B3C;font-style: normal;font-weight: 400;font-size: 12px;line-height: 150%;">
236
+ <p>
237
+ © Tango Eye. All rights reserved.</p>
238
+ </div>
239
+ </td>
240
+ </tr>
241
+ </table>
242
+ </td>
243
+ </tr>
244
+
245
+ </table>
246
+
247
+ </body>
248
+
249
+ </html>