tango-app-api-infra 3.0.39-dev → 3.0.40-dev

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.0.39-dev",
3
+ "version": "3.0.40-dev",
4
4
  "description": "infra",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -17,13 +17,14 @@
17
17
  "cors": "^2.8.5",
18
18
  "dayjs": "^1.11.10",
19
19
  "dotenv": "^16.4.5",
20
+ "excel4node": "^1.8.2",
20
21
  "express": "^4.18.3",
21
22
  "express-fileupload": "^1.5.0",
22
23
  "handlebars": "^4.7.8",
23
24
  "mongodb": "^6.4.0",
24
25
  "nodemon": "^3.1.0",
25
- "tango-api-schema": "^2.0.69",
26
- "tango-app-api-middleware": "^1.0.52-dev",
26
+ "tango-api-schema": "^2.0.77",
27
+ "tango-app-api-middleware": "^1.0.54-dev",
27
28
  "winston": "^3.12.0",
28
29
  "winston-daily-rotate-file": "^5.0.0"
29
30
  },
@@ -2,11 +2,15 @@
2
2
 
3
3
  import { createTangoTicket, findOneTangoTicket, updateOneTangoTicket } from '../services/tangoTicket.service.js';
4
4
  import { createinfraReason, findinfraReason } from '../services/infraReason.service.js';
5
- import { updateOneStore } from '../services/store.service.js';
6
- import { logger, fileUpload, signedUrl } from 'tango-app-api-middleware';
5
+ import { updateOneStore, findStore } from '../services/store.service.js';
6
+ import { logger, fileUpload, signedUrl, sendEmailWithSES, getOpenSearchData, appConfig } from 'tango-app-api-middleware';
7
7
  import { aggregateUser, updateOneUser } from '../services/user.service.js';
8
8
  import { updateoneClient } from '../services/client.service.js';
9
9
  import dayjs from 'dayjs';
10
+ import { readFileSync } from 'fs';
11
+ import { join } from 'path';
12
+ import handlebars from 'handlebars';
13
+ import { findOneGroup } from '../services/group.service.js';
10
14
  export async function createTicket( req, res ) {
11
15
  try {
12
16
  req.body.issueDate = new Date( req.body.Date );
@@ -22,7 +26,55 @@ export async function createTicket( req, res ) {
22
26
  if ( req.body.issueType == 'installation' ) {
23
27
  req.body.ticketId = 'TE_INS_' + new Date().valueOf();
24
28
  }
29
+
30
+
31
+ let downTimeQuery = {
32
+ 'size': 1,
33
+ 'query': {
34
+ 'bool': {
35
+ 'must': [
36
+ {
37
+ 'term': {
38
+ 'doc.date.keyword': dayjs( req.body.issueDate ).format( 'DD-MM-YYYY' ),
39
+ },
40
+ },
41
+ {
42
+ 'term': {
43
+ 'doc.store_id.keyword': req.body.basicDetails.storeId,
44
+ },
45
+ },
46
+
47
+ ],
48
+
49
+ },
50
+ },
51
+ };
52
+ let downtimetotal;
53
+ const downtime = await getOpenSearchData( 'live_downtime_hourly', downTimeQuery );
54
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
55
+ if ( streamwiseDowntime.length > 0 ) {
56
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
57
+ return accumulator + currentValue.down_time;
58
+ }, 0 );
59
+ const average = sum / streamwiseDowntime.length;
60
+ downtimetotal = Math.round( average );
61
+ } else {
62
+ downtimetotal = 0;
63
+ }
64
+
65
+
25
66
  let create = await createTangoTicket( req.body );
67
+ let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
68
+ const attachments = null;
69
+ const subject = `Tango Eye - Ticket Created for ${req.body.basicDetails.storeName}`;
70
+ const fileContent = readFileSync( join() + '/src/hbs/createTicket.hbs', 'utf8' );
71
+ const htmlContent = handlebars.compile( fileContent );
72
+ let Uidomain = `${appConfig.url.domain}/manage/stores/infra-ticket?storeId=${req.body.basicDetails.storeId}`;
73
+
74
+ const html = htmlContent( { ...req.body, Uidomain: Uidomain, domain: appConfig.url.apiDomain, date: dayjs( req.body.issueDate ).format( 'YYYY-MM-DD' ), downtimetotal: downtimetotal, Timestamp: Timestamp } );
75
+ await sendEmailWithSES( req.body.spocEmail, subject, html, attachments, appConfig.cloud.aws.ses.adminEmail );
76
+
77
+
26
78
  if ( create ) {
27
79
  res.sendSuccess( 'Ticket Created Successfully' );
28
80
  }
@@ -148,7 +200,29 @@ export async function secondaryReason( req, res ) {
148
200
 
149
201
  export async function updateTicketIssue( req, res ) {
150
202
  try {
151
- let updateTicket = await updateOneTangoTicket( { ticketId: req.body.ticketId }, { 'ticketActivity': req.body.ticketActivity, 'cameraList': req.body.cameraList, 'ticketDetails.issueIdentifiedDate': new Date(), 'ticketDetails.issueIdentifiedBy': req.body.issueIdentifiedBy, 'ticketDetails.issueStatus': 'identified', 'status': 'inprogress' } );
203
+ let query = {};
204
+ if ( req.user.userType == 'client' ) {
205
+ query = {
206
+ 'ticketActivity': req.body.ticketActivity,
207
+ 'cameraList': req.body.cameraList,
208
+ 'ticketDetails.issueIdentifiedDate': new Date(),
209
+ 'ticketDetails.issueIdentifiedBy': req.body.issueIdentifiedBy,
210
+ 'ticketDetails.addressingClient': req.user._id,
211
+ 'ticketDetails.issueStatus': 'identified',
212
+ 'status': 'inprogress',
213
+ };
214
+ } else {
215
+ query = {
216
+ 'ticketActivity': req.body.ticketActivity,
217
+ 'cameraList': req.body.cameraList,
218
+ 'ticketDetails.issueIdentifiedDate': new Date(),
219
+ 'ticketDetails.issueIdentifiedBy': req.body.issueIdentifiedBy,
220
+ 'ticketDetails.addressingUser': req.user._id,
221
+ 'ticketDetails.issueStatus': 'identified',
222
+ 'status': 'inprogress',
223
+ };
224
+ }
225
+ let updateTicket = await updateOneTangoTicket( { ticketId: req.body.ticketId }, query );
152
226
  if ( req.body.ticketDetails.ticketType === 'refreshticket' ) {
153
227
  await updateOneTangoTicket( { ticketId: req.body.ticketId }, { 'ticketDetails.refreshTicketStatus': 'identified' } );
154
228
  }
@@ -332,12 +406,15 @@ export async function emailUserList( req, res ) {
332
406
  {
333
407
  $group: {
334
408
  _id: null,
409
+ assignedValue: { $push: '$assignedValue' },
410
+ assignedType: { $first: '$assignedType' },
335
411
  storeList: { $push: '$assignedValue' },
336
412
  },
337
413
  },
338
414
  {
339
415
  $project: {
340
-
416
+ assignedType: 1,
417
+ assignedValue: 1,
341
418
  assignedStore: { $size: '$storeList' },
342
419
  },
343
420
  },
@@ -353,6 +430,8 @@ export async function emailUserList( req, res ) {
353
430
  email: 1,
354
431
  role: 1,
355
432
  emailAlert: 1,
433
+ assignedValue: '$assigned.assignedValue',
434
+ assignedType: { $ifNull: [ '$assigned.assignedType', 0 ] },
356
435
  infraAlert: { $ifNull: [ '$emailAlert.infra', false ] },
357
436
  assigned: { $ifNull: [ '$assigned.assignedStore', 0 ] },
358
437
  isActive: 1,
@@ -384,6 +463,25 @@ export async function emailUserList( req, res ) {
384
463
  );
385
464
  }
386
465
  const result = await aggregateUser( query );
466
+ for ( let user of result ) {
467
+ if ( user.role == 'superadmin' ) {
468
+ let storelist = await findStore( { clientId: user.clientId, status: 'active' }, { storeName: 1 } );
469
+ user.assigned = storelist.length;
470
+ user.assignedValue = storelist;
471
+ } else if ( user.role != 'superadmin' ) {
472
+ if ( user.assignedType == 'group' ) {
473
+ for ( let group of user.assignedValue ) {
474
+ let groupdata = await findOneGroup( { groupName: group } );
475
+ let storelist = await findStore( { storeId: { $in: groupdata.storeList } }, { storeName: 1 } );
476
+ user.assigned = storelist.length;
477
+ user.assignedValue = storelist;
478
+ }
479
+ } else if ( user.assignedType == 'store' ) {
480
+ let storelist = await findStore( { storeId: { $in: user.assignedValue } }, { storeName: 1 } );
481
+ user.assignedValue = storelist;
482
+ }
483
+ }
484
+ }
387
485
  res.sendSuccess( { count: count.length, result: result } );
388
486
  } catch ( error ) {
389
487
  logger.error( { error: error, function: 'emailUserList' } );
@@ -394,13 +492,13 @@ export async function saveInfraEmailConfig( req, res ) {
394
492
  try {
395
493
  let inputData = req.body;
396
494
 
397
- let clientupdate = await updateoneClient( { clientId: inputData.clientId }, {
495
+ await updateoneClient( { clientId: inputData.clientId }, {
398
496
  'ticketConfigs.infraReport': {
399
497
  start: inputData.start,
400
498
  end: inputData.end,
401
499
  },
402
500
  } );
403
- console.log( clientupdate );
501
+
404
502
  if ( inputData.userList && inputData.userList.length > 0 ) {
405
503
  for ( let user of inputData.userList ) {
406
504
  await updateOneUser( { email: user.email }, {
@@ -1,16 +1,22 @@
1
1
 
2
- import { logger } from 'tango-app-api-middleware';
2
+ import { logger, getOpenSearchData, appConfig } from 'tango-app-api-middleware';
3
3
  import dayjs from 'dayjs';
4
4
  import utc from 'dayjs/plugin/utc.js';
5
5
  import timezone from 'dayjs/plugin/timezone.js';
6
6
  import 'dayjs/locale/en.js';
7
+ import { readFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import handlebars from 'handlebars';
7
10
  dayjs.extend( utc );
8
11
  dayjs.extend( timezone );
12
+ import { sendEmailWithSES } from 'tango-app-api-middleware';
9
13
  import { createClient, findClient, aggregateClient } from '../services/client.service.js';
10
- import { createStore, findStore, updateOneStore } from '../services/store.service.js';
11
- import { findTangoTicket, findOneTangoTicket, updateOneTangoTicket } from '../services/tangoTicket.service.js';
14
+ import { createStore, findStore, updateOneStore, findOneStore } from '../services/store.service.js';
15
+ import { findTangoTicket, findOneTangoTicket, countDocumentsTangoTicket, aggregateTangoTicket, updateOneTangoTicket } from '../services/tangoTicket.service.js';
12
16
  import { findOneGroup } from '../services/group.service.js';
13
-
17
+ import { findinfraReason } from '../services/infraReason.service.js';
18
+ import { findOneUser } from '../services/user.service.js';
19
+ import xl from 'excel4node';
14
20
 
15
21
  export async function migrateClient() {
16
22
  try {
@@ -144,8 +150,61 @@ export async function assigntoUser( req, res ) {
144
150
  export async function updateRefreshTicket( req, res ) {
145
151
  try {
146
152
  for ( let ticket of req.body.TicketList ) {
153
+ let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
147
154
  await updateOneTangoTicket( { ticketId: ticket.ticketId }, { 'ticketDetails.ticketType': 'refreshticket' } );
155
+ let downTimeQuery = {
156
+ 'size': 1,
157
+ 'query': {
158
+ 'bool': {
159
+ 'must': [
160
+ {
161
+ 'term': {
162
+ 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
163
+ },
164
+ },
165
+ {
166
+ 'term': {
167
+ 'doc.store_id.keyword': getTicket.basicDetails.storeId,
168
+ },
169
+ },
170
+
171
+ ],
172
+
173
+ },
174
+ },
175
+ };
176
+ let downtimetotal;
177
+ const downtime = await getOpenSearchData( 'live_downtime_hourly', downTimeQuery );
178
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
179
+ if ( streamwiseDowntime.length > 0 ) {
180
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
181
+ return accumulator + currentValue.down_time;
182
+ }, 0 );
183
+ const average = sum / streamwiseDowntime.length;
184
+ downtimetotal = Math.round( average );
185
+ } else {
186
+ downtimetotal = 0;
187
+ }
188
+ let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
189
+ const attachments = null;
190
+ const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
191
+ const fileContent = readFileSync( join() + '/src/hbs/refreshTicket.hbs', 'utf8' );
192
+ const htmlContent = handlebars.compile( fileContent );
193
+ let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
194
+ if ( store.spocDetails && store.spocDetails.length > 0 ) {
195
+ let spocEmail = store.spocDetails[0].email;
196
+ let spocName = store.spocDetails[0].name;
197
+ let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
198
+ let primaryIssue = 'Not Identified';
199
+ if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
200
+ primaryIssue = Issue[0].reasons[0].primaryIssue;
201
+ }
202
+ let Uidomain = `${appConfig.url.domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
203
+ const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primary: primaryIssue, storeName: getTicket.basicDetails.storeName, hibernation: '', spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: appConfig.url.apiDomain } );
204
+ await sendEmailWithSES( spocEmail, subject, html, attachments, appConfig.cloud.aws.ses.adminEmail );
205
+ }
148
206
  }
207
+
149
208
  res.sendSuccess( 'updated Successfully' );
150
209
  } catch ( error ) {
151
210
  logger.error( { error: error, function: 'updateRefreshTicket' } );
@@ -169,7 +228,61 @@ export async function closeTicket( req, res ) {
169
228
  issueClosedDate: new Date(),
170
229
  },
171
230
  );
231
+ let downTimeQuery = {
232
+ 'size': 1,
233
+ 'query': {
234
+ 'bool': {
235
+ 'must': [
236
+ {
237
+ 'term': {
238
+ 'doc.date.keyword': dayjs( getTicket.issueDate ).format( 'DD-MM-YYYY' ),
239
+ },
240
+ },
241
+ {
242
+ 'term': {
243
+ 'doc.store_id.keyword': getTicket.basicDetails.storeId,
244
+ },
245
+ },
246
+
247
+ ],
248
+
249
+ },
250
+ },
251
+ };
252
+ let downtimetotal;
253
+ const downtime = await getOpenSearchData( 'live_downtime_hourly', downTimeQuery );
254
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
255
+ if ( streamwiseDowntime.length > 0 ) {
256
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
257
+ return accumulator + currentValue.down_time;
258
+ }, 0 );
259
+ const average = sum / streamwiseDowntime.length;
260
+ downtimetotal = Math.round( average );
261
+ } else {
262
+ downtimetotal = 0;
263
+ }
264
+
265
+ let Issue = getTicket.ticketActivity.filter( ( a ) => a.actionType == 'issueUpdate' );
266
+ let primaryIssue = '';
267
+
268
+ if ( Issue.length > 0 && Issue[0].reasons.length > 0 ) {
269
+ primaryIssue = Issue[0].reasons[0].primaryIssue;
270
+ }
271
+ let Timestamp = dayjs().format( 'YYYY-MM-DD HH:mm' );
272
+ const attachments = null;
273
+ const subject = `Tango Eye - Infra Ticket Closed for ${getTicket.basicDetails.storeName} `;
274
+ const fileContent = readFileSync( join() + '/src/hbs/closeTicekt.hbs', 'utf8' );
275
+ const htmlContent = handlebars.compile( fileContent );
276
+ let store = await findOneStore( { storeId: getTicket.basicDetails.storeId } );
277
+ if ( store.spocDetails && store.spocDetails.length > 0 ) {
278
+ let spocEmail = store.spocDetails[0].email;
279
+ let spocName = store.spocDetails[0].name;
280
+ let Uidomain = `${appConfig.url.domain}/manage/stores/infra-ticket?storeId=${getTicket.basicDetails.storeId}`;
281
+ const html = htmlContent( { ...getTicket, Uidomain: Uidomain, primaryIssue: primaryIssue, storeName: getTicket.basicDetails.storeName, spocName: spocName, date: dayjs( getTicket.issueDate ).format( 'YYYY-MM-DD HH:mm' ), downtimetotal: downtimetotal, Timestamp: Timestamp, domain: appConfig.url.apiDomain } );
282
+ await sendEmailWithSES( spocEmail, subject, html, attachments, appConfig.cloud.aws.ses.adminEmail );
283
+ }
172
284
  }
285
+
173
286
  res.sendSuccess( 'updated Successfully' );
174
287
  } catch ( error ) {
175
288
  logger.error( { error: error, function: 'closeTicket' } );
@@ -260,6 +373,7 @@ export async function emailUserList( req, res ) {
260
373
  'clientId': 1,
261
374
  'ticketConfigs': 1,
262
375
  'email': '$user.email',
376
+ 'userName': '$user.userName',
263
377
  'role': '$user.role',
264
378
  'assignedValue': '$assignedstore.assignedValue',
265
379
  'assignedType': '$assignedstore.assignedType',
@@ -271,6 +385,7 @@ export async function emailUserList( req, res ) {
271
385
  'clientId': { $first: '$clientId' },
272
386
  'ticketConfigs': { $first: '$ticketConfigs' },
273
387
  'email': { $first: '$email' },
388
+ 'userName': { $first: '$userName' },
274
389
  'role': { $first: '$role' },
275
390
  'assignedValue': { $push: '$assignedValue' },
276
391
  'assignedType': { $first: '$assignedType' },
@@ -280,7 +395,7 @@ export async function emailUserList( req, res ) {
280
395
  let response = [];
281
396
  for ( let user of clientList ) {
282
397
  if ( user.role == 'superadmin' ) {
283
- let storelist = await findStore( { clientId: user.clientId }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
398
+ let storelist = await findStore( { clientId: user.clientId, status: 'active' }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
284
399
  user.storeList = storelist;
285
400
  } else if ( user.role != 'superadmin' ) {
286
401
  if ( user.assignedType == 'store' ) {
@@ -296,9 +411,11 @@ export async function emailUserList( req, res ) {
296
411
  }
297
412
  }
298
413
  }
299
- if ( user.storeList&&user.storeList.length>0&&user.ticketConfigs&&user.ticketConfigs.infraReport ) {
414
+
415
+ if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
300
416
  response.push( {
301
417
  email: user.email,
418
+ userName: user.userName,
302
419
  clientId: user.clientId,
303
420
  role: user.role,
304
421
  storeList: user.storeList,
@@ -312,3 +429,213 @@ export async function emailUserList( req, res ) {
312
429
  res.sendError( error, 500 );
313
430
  }
314
431
  }
432
+ export async function infraReportSent( req, res ) {
433
+ try {
434
+ let date;
435
+ if ( req.body.type == 'start' ) {
436
+ date = dayjs().subtract( 1, 'day' ).format( 'YYYY-MM-DD' );
437
+ } else if ( req.body.storeList == 'start' ) {
438
+ date = dayjs().format( 'YYYY-MM-DD' );
439
+ };
440
+
441
+ let query = [ {
442
+ $match: {
443
+ $and: [
444
+ { issueDate: new Date( date ) },
445
+ { 'basicDetails.storeId': { $in: req.body.storeList } },
446
+ { issueType: 'infra' },
447
+ { 'ticketDetails.issueStatus': 'identified' },
448
+ ],
449
+ },
450
+ },
451
+ {
452
+ $project: {
453
+ ticketId: 1,
454
+ basicDetails: 1,
455
+ createdAt: 1,
456
+ status: 1,
457
+ ticketDetails: 1,
458
+ issueClosedDate: 1,
459
+ primaryIssue: {
460
+ $filter: {
461
+ input: '$ticketActivity',
462
+ as: 'item',
463
+ cond: { $eq: [ '$$item.actionType', 'issueUpdate' ] },
464
+ },
465
+ },
466
+ },
467
+ },
468
+ {
469
+ $unwind: {
470
+ path: '$primaryIssue', preserveNullAndEmptyArrays: true,
471
+ },
472
+ },
473
+ {
474
+ $unwind: {
475
+ path: '$primaryIssue.reasons', preserveNullAndEmptyArrays: true,
476
+ },
477
+ },
478
+ {
479
+ $unwind: {
480
+ path: '$primaryIssue.reasons.secondaryIssue', preserveNullAndEmptyArrays: true,
481
+ },
482
+ },
483
+ {
484
+ $project: {
485
+ ticketId: 1,
486
+ basicDetails: 1,
487
+ createdAt: 1,
488
+ status: 1,
489
+ ticketDetails: 1,
490
+ issueClosedDate: 1,
491
+ primaryIssue: { $ifNull: [ '$primaryIssue.reasons.primaryIssue', '-' ] },
492
+ secondaryIssue: { $ifNull: [ '$primaryIssue.reasons.secondaryIssue.name', '-' ] },
493
+ },
494
+ },
495
+ {
496
+ $group: {
497
+ _id: '$ticketId',
498
+ ticketDetails: { $first: '$ticketDetails' },
499
+ issueClosedDate: { $first: '$issueClosedDate' },
500
+ basicDetails: { $first: '$basicDetails' },
501
+ primaryIssue: { $last: '$primaryIssue' },
502
+ secondaryIssue: { $last: '$secondaryIssue' },
503
+ createdAt: { $first: '$createdAt' },
504
+ status: { $last: '$status' },
505
+
506
+ },
507
+ },
508
+ ];
509
+
510
+ let ticketList = await aggregateTangoTicket( query );
511
+ const exportdata = [];
512
+ for ( let element of ticketList ) {
513
+ let clientuser = await findOneUser( { _id: element.ticketDetails.addressingClient } );
514
+ let tangouser = await findOneUser( { _id: element.ticketDetails.addressingUser } );
515
+ exportdata.push( {
516
+ 'Client ID': element.basicDetails.clientId,
517
+ 'Client Name': element.basicDetails.clientName,
518
+ 'Created Date & Time': dayjs( element.createdAt ).format( 'YYYY-MM-DD HH:mm A' ),
519
+ 'Store ID': element.basicDetails.clientName,
520
+ 'Store Name': element.basicDetails.clientName,
521
+ 'Issue ': element.primaryIssue,
522
+ 'Secondary Issue': element.secondaryIssue,
523
+ 'Issue Date & Time': dayjs( element.date ).format( 'YYYY-MM-DD' ),
524
+ 'Status ': element.status,
525
+ 'Responded By': clientuser && clientuser.userName ? clientuser.userName : '-',
526
+ 'Resolved By': tangouser && tangouser.userName ? tangouser.userName : '-',
527
+ 'Closed Date & Time': element.issueClosedDate?dayjs( element.issueClosedDate ).format( 'YYYY-MM-DD HH:mm A' ):'-',
528
+ 'Latest Comment': '',
529
+ 'Activity Log': '',
530
+ } );
531
+ }
532
+ let issueCount = await countDocumentsTangoTicket( {
533
+
534
+ $and: [
535
+ { issueDate: new Date( date ) },
536
+ { 'basicDetails.storeId': { $in: req.body.storeList } },
537
+ { issueType: 'infra' },
538
+ { 'ticketDetails.issueStatus': 'identified' },
539
+ ],
540
+
541
+ } );
542
+ let finalcount = 0;
543
+ for ( let storedata of req.body.storeList ) {
544
+ let downTimeQuery = {
545
+ 'size': 1,
546
+ 'query': {
547
+ 'bool': {
548
+ 'must': [
549
+ {
550
+ 'term': {
551
+ 'doc.date.keyword': dayjs( date ).format( 'DD-MM-YYYY' ),
552
+ },
553
+ },
554
+ {
555
+ 'term': {
556
+ 'doc.store_id.keyword': storedata,
557
+ },
558
+ },
559
+
560
+ ],
561
+
562
+ },
563
+ },
564
+ };
565
+ let downtimetotal;
566
+ const downtime = await getOpenSearchData( 'live_downtime_hourly', downTimeQuery );
567
+ let streamwiseDowntime = downtime.body.hits.hits.length > 0 ? downtime.body.hits.hits[0]._source.doc.streamwise_downtime : [];
568
+ if ( streamwiseDowntime.length > 0 ) {
569
+ const sum = streamwiseDowntime.reduce( ( accumulator, currentValue ) => {
570
+ return accumulator + currentValue.down_time;
571
+ }, 0 );
572
+ const average = sum / streamwiseDowntime.length;
573
+ downtimetotal = Math.round( average );
574
+ } else {
575
+ downtimetotal = 0;
576
+ }
577
+
578
+ finalcount = finalcount + downtimetotal;
579
+ }
580
+ let avgDownTime = Math.round( finalcount / req.body.storeList.length );
581
+
582
+
583
+ let issueList = await findinfraReason( { parentId: { '$exists': false } } );
584
+ const categoryCounts = {};
585
+ let response;
586
+ if ( ticketList.length > 0 ) {
587
+ ticketList.forEach( ( item ) => {
588
+ const categoryName = item.primaryIssue;
589
+ if ( categoryCounts[categoryName] ) {
590
+ categoryCounts[categoryName]++;
591
+ } else {
592
+ categoryCounts[categoryName] = 1;
593
+ }
594
+ } );
595
+ response = issueList.map( ( category ) => ( {
596
+ name: category.name,
597
+ count: categoryCounts[category.name] || 0,
598
+ } ) );
599
+ } else {
600
+ response = issueList.map( ( category ) => ( {
601
+ name: category.name,
602
+ count: 0,
603
+ } ) );
604
+ }
605
+ let reportdate = dayjs().format( 'YYYY-MM-DD' );
606
+ const wb = new xl.Workbook();
607
+ const ws = wb.addWorksheet( 'Daily report' );
608
+ const headers = Object.keys( exportdata[0] );
609
+
610
+ for ( let i = 0; i < headers.length; i++ ) {
611
+ ws.cell( 1, i + 1 ).string( headers[i] );
612
+ };
613
+ for ( let i = 0; i < exportdata.length; i++ ) {
614
+ const dataRow = exportdata[i];
615
+ for ( let j = 0; j < headers.length; j++ ) {
616
+ const header = headers[j];
617
+ const value = dataRow[header];
618
+ ws.cell( i + 2, j + 1 ).string( value?.toString() );
619
+ }
620
+ }
621
+ let buffer = await wb.writeToBuffer();
622
+
623
+ const attachments = {
624
+ filename: `dailyInfraReport- ${reportdate}.xlsx`,
625
+ content: buffer,
626
+ contentType: 'application/xlsx', // e.g., 'application/pdf'
627
+ };
628
+ const subject = `Daily Digest - Infra Downtime Report - ${reportdate}`;
629
+ const fileContent = readFileSync( join() + '/src/hbs/dailyInfraReport.hbs', 'utf8' );
630
+ const htmlContent = handlebars.compile( fileContent );
631
+ let Uidomain = `${appConfig.url.domain}`;
632
+
633
+
634
+ const html = htmlContent( { ...req.body, Uidomain: Uidomain, issueCount: issueCount, avgDownTime: avgDownTime, reportdate: reportdate, content: response, date: date, domain: appConfig.url.apiDomain } );
635
+ const result = await sendEmailWithSES( req.body.email, subject, html, attachments, appConfig.cloud.aws.ses.adminEmail );
636
+ res.sendSuccess( result );
637
+ } catch ( error ) {
638
+ logger.error( { error: error, function: 'infraReportSent' } );
639
+ res.sendError( error, 500 );
640
+ }
641
+ }