tango-app-api-infra 3.0.38-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.
@@ -1,15 +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 );
9
- import { createClient, findClient } 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';
12
-
12
+ import { sendEmailWithSES } from 'tango-app-api-middleware';
13
+ import { createClient, findClient, aggregateClient } from '../services/client.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';
16
+ import { findOneGroup } from '../services/group.service.js';
17
+ import { findinfraReason } from '../services/infraReason.service.js';
18
+ import { findOneUser } from '../services/user.service.js';
19
+ import xl from 'excel4node';
13
20
 
14
21
  export async function migrateClient() {
15
22
  try {
@@ -143,8 +150,61 @@ export async function assigntoUser( req, res ) {
143
150
  export async function updateRefreshTicket( req, res ) {
144
151
  try {
145
152
  for ( let ticket of req.body.TicketList ) {
153
+ let getTicket = await findOneTangoTicket( { ticketId: ticket.ticketId } );
146
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
+ }
147
206
  }
207
+
148
208
  res.sendSuccess( 'updated Successfully' );
149
209
  } catch ( error ) {
150
210
  logger.error( { error: error, function: 'updateRefreshTicket' } );
@@ -168,10 +228,414 @@ export async function closeTicket( req, res ) {
168
228
  issueClosedDate: new Date(),
169
229
  },
170
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
+ }
171
284
  }
285
+
172
286
  res.sendSuccess( 'updated Successfully' );
173
287
  } catch ( error ) {
174
288
  logger.error( { error: error, function: 'closeTicket' } );
175
289
  res.sendError( error, 500 );
176
290
  }
177
291
  }
292
+ export async function emailUserList( req, res ) {
293
+ try {
294
+ let clientList = await aggregateClient( [
295
+ {
296
+ $match: {
297
+ status: 'active',
298
+ // clientId:{ req.body.clientId},
299
+ },
300
+ },
301
+ {
302
+ $project: {
303
+ 'clientId': 1,
304
+ 'ticketConfigs.infraReport': 1,
305
+ },
306
+ },
307
+ {
308
+ '$lookup': {
309
+ 'from': 'users',
310
+ 'let': { 'clientId': '$clientId' },
311
+ 'pipeline': [
312
+ {
313
+ '$match': {
314
+ '$expr': {
315
+ $and: [
316
+ { '$eq': [ '$clientId', '$$clientId' ] },
317
+ { '$eq': [ '$userType', 'client' ] },
318
+
319
+ ],
320
+ },
321
+ },
322
+ },
323
+ {
324
+ $project: {
325
+ userName: 1,
326
+ email: 1,
327
+ role: 1,
328
+ },
329
+ },
330
+ ],
331
+ 'as': 'user',
332
+ },
333
+ },
334
+ {
335
+ $unwind: {
336
+ path: '$user',
337
+ preserveNullAndEmptyArrays: true,
338
+ },
339
+ },
340
+ {
341
+ '$lookup': {
342
+ 'from': 'userAssignedStore',
343
+ 'let': { 'email': '$user.email' },
344
+ 'pipeline': [
345
+ {
346
+ '$match': {
347
+ '$expr': {
348
+ $and: [
349
+ { '$eq': [ '$userEmail', '$$email' ] },
350
+
351
+ ],
352
+ },
353
+ },
354
+ },
355
+ {
356
+ $project: {
357
+ assignedValue: 1,
358
+ assignedType: 1,
359
+ },
360
+ },
361
+ ],
362
+ 'as': 'assignedstore',
363
+ },
364
+ },
365
+ {
366
+ $unwind: {
367
+ path: '$assignedstore',
368
+ preserveNullAndEmptyArrays: true,
369
+ },
370
+ },
371
+ {
372
+ $project: {
373
+ 'clientId': 1,
374
+ 'ticketConfigs': 1,
375
+ 'email': '$user.email',
376
+ 'userName': '$user.userName',
377
+ 'role': '$user.role',
378
+ 'assignedValue': '$assignedstore.assignedValue',
379
+ 'assignedType': '$assignedstore.assignedType',
380
+ },
381
+ },
382
+ {
383
+ $group: {
384
+ '_id': '$email',
385
+ 'clientId': { $first: '$clientId' },
386
+ 'ticketConfigs': { $first: '$ticketConfigs' },
387
+ 'email': { $first: '$email' },
388
+ 'userName': { $first: '$userName' },
389
+ 'role': { $first: '$role' },
390
+ 'assignedValue': { $push: '$assignedValue' },
391
+ 'assignedType': { $first: '$assignedType' },
392
+ },
393
+ },
394
+ ] );
395
+ let response = [];
396
+ for ( let user of clientList ) {
397
+ if ( user.role == 'superadmin' ) {
398
+ let storelist = await findStore( { clientId: user.clientId, status: 'active' }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
399
+ user.storeList = storelist;
400
+ } else if ( user.role != 'superadmin' ) {
401
+ if ( user.assignedType == 'store' ) {
402
+ let storelist = await findStore( { storeId: { $in: user.assignedValue } }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
403
+ user.storeList = storelist;
404
+ }
405
+ if ( user.assignedType == 'group' ) {
406
+ user.storeList = [];
407
+ for ( let group of user.assignedValue ) {
408
+ let groupdata = await findOneGroup( { groupName: group } );
409
+ let storelist = await findStore( { storeId: { $in: groupdata.storeList } }, { 'storeId': 1, 'storeProfile.timeZone': 1 } );
410
+ user.storeList = [ ...user.storeList, ...storelist ];
411
+ }
412
+ }
413
+ }
414
+
415
+ if ( user.storeList && user.storeList.length > 0 && user.ticketConfigs && user.ticketConfigs.infraReport ) {
416
+ response.push( {
417
+ email: user.email,
418
+ userName: user.userName,
419
+ clientId: user.clientId,
420
+ role: user.role,
421
+ storeList: user.storeList,
422
+ ticketConfigs: user.ticketConfigs.infraReport,
423
+ } );
424
+ }
425
+ }
426
+ res.sendSuccess( { count: response.length, response: response } );
427
+ } catch ( error ) {
428
+ logger.error( { error: error, function: 'emailUserList' } );
429
+ res.sendError( error, 500 );
430
+ }
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
+ }