tango-app-api-trax 1.0.0-alpha.2 → 1.0.0-alpha.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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
 
3
3
  import { traxDashboardRouter } from './src/routes/traxDashboard.routes.js';
4
+ import { traxFlagRouter } from './src/routes/traxFlag.router.js';
5
+ import { traxRouter } from './src/routes/trax.routes.js';
4
6
 
5
- export { traxDashboardRouter };
6
-
7
+ export { traxDashboardRouter, traxFlagRouter, traxRouter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-trax",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -14,9 +14,11 @@
14
14
  "license": "ISC",
15
15
  "dependencies": {
16
16
  "aws-sdk": "^2.1665.0",
17
+ "body-parser": "^1.20.3",
17
18
  "dayjs": "^1.11.13",
18
19
  "dotenv": "^16.4.5",
19
- "express": "^4.19.2",
20
+ "express": "^4.21.1",
21
+ "express-fileupload": "^1.5.1",
20
22
  "handlebars": "^4.7.8",
21
23
  "lodash": "^4.17.21",
22
24
  "mongodb": "^6.8.0",
@@ -0,0 +1,460 @@
1
+ import { download, logger } from 'tango-app-api-middleware';
2
+ import { aggregate } from '../services/processedchecklist.services.js';
3
+ import dayjs from 'dayjs';
4
+
5
+ import utc from 'dayjs/plugin/utc.js';
6
+ dayjs.extend( utc );
7
+
8
+
9
+ export const overallFlagMetrics = async ( req, res ) => {
10
+ try {
11
+ const pipeline = [
12
+ {
13
+ $facet: {
14
+ currentPeriod: [
15
+ {
16
+ $match: {
17
+ $and: [
18
+ { client_id: req.body.clientId },
19
+ {
20
+ store_id: {
21
+ $in: req.body.stores,
22
+ },
23
+ },
24
+ {
25
+ date_iso: {
26
+ $gte: new Date( req.body.startDate ),
27
+ },
28
+ },
29
+ {
30
+ date_iso: {
31
+ $lte: new Date( req.body.endDate ),
32
+ },
33
+ },
34
+ ],
35
+ },
36
+ },
37
+ {
38
+ $project: {
39
+ timeFlag: 1,
40
+ questionFlag: 1,
41
+ mobileDetectionFlag: 1,
42
+ storeOpenCloseFlag: 1,
43
+ uniformDetectionFlag: 1,
44
+ },
45
+ },
46
+ {
47
+ $group: {
48
+ _id: '',
49
+ questionFlag: {
50
+ $sum: '$questionFlag',
51
+ },
52
+ timeFlag: { $sum: '$timeFlag' },
53
+ detectionFlags: {
54
+ $sum: {
55
+ $cond: [
56
+ {
57
+ $or: [
58
+ {
59
+ $gt: [
60
+ '$mobileDetectionFlag',
61
+ 0,
62
+ ],
63
+ },
64
+ {
65
+ $gt: [
66
+ '$storeOpenCloseFlag',
67
+ 0,
68
+ ],
69
+ },
70
+ {
71
+ $gt: [
72
+ '$uniformDetectionFlag',
73
+ 0,
74
+ ],
75
+ },
76
+ ],
77
+ },
78
+ 1,
79
+ 0,
80
+ ],
81
+ },
82
+ },
83
+ },
84
+ },
85
+ {
86
+ $addFields: {
87
+ totalFlags: {
88
+ $add: [
89
+ '$questionFlag',
90
+ '$timeFlag',
91
+ '$detectionFlags',
92
+ ],
93
+ },
94
+ },
95
+ },
96
+ {
97
+ $project: {
98
+ _id: 0,
99
+ },
100
+ },
101
+ ],
102
+ last7Days: [
103
+ {
104
+ $match: {
105
+ $and: [
106
+ { client_id: req.body.clientId },
107
+ {
108
+ store_id: {
109
+ $in: req.body.stores,
110
+ },
111
+ },
112
+ {
113
+ date_iso: {
114
+ $gte: dayjs.utc( req.body.endDate ).subtract( 7, 'days' ).startOf( 'day' ).toDate(),
115
+ },
116
+ },
117
+ {
118
+ date_iso: {
119
+ $lte: dayjs.utc( req.body.startDate ).subtract( 1, 'day' ).startOf( 'day' ).toDate(),
120
+ },
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ {
126
+ $project: {
127
+ timeFlag: 1,
128
+ questionFlag: 1,
129
+ mobileDetectionFlag: 1,
130
+ storeOpenCloseFlag: 1,
131
+ uniformDetectionFlag: 1,
132
+ },
133
+ },
134
+ {
135
+ $group: {
136
+ _id: '',
137
+ questionFlag: {
138
+ $sum: '$questionFlag',
139
+ },
140
+ timeFlag: { $sum: '$timeFlag' },
141
+ detectionFlags: {
142
+ $sum: {
143
+ $cond: [
144
+ {
145
+ $or: [
146
+ {
147
+ $gt: [
148
+ '$mobileDetectionFlag',
149
+ 0,
150
+ ],
151
+ },
152
+ {
153
+ $gt: [
154
+ '$storeOpenCloseFlag',
155
+ 0,
156
+ ],
157
+ },
158
+ {
159
+ $gt: [
160
+ '$uniformDetectionFlag',
161
+ 0,
162
+ ],
163
+ },
164
+ ],
165
+ },
166
+ 1,
167
+ 0,
168
+ ],
169
+ },
170
+ },
171
+ },
172
+ },
173
+ // {
174
+ // $addFields: {
175
+ // questionFlag: {
176
+ // $round: [
177
+ // { $divide: [ '$questionFlag', 7 ] },
178
+ // 0,
179
+ // ],
180
+ // },
181
+ // timeFlag: {
182
+ // $round: [
183
+ // { $divide: [ '$timeFlag', 7 ] },
184
+ // 0,
185
+ // ],
186
+ // },
187
+ // detectionFlags: {
188
+ // $round: [
189
+ // {
190
+ // $divide: [ '$detectionFlags', 7 ],
191
+ // },
192
+ // 0,
193
+ // ],
194
+ // },
195
+ // },
196
+ // },
197
+ ],
198
+ },
199
+ },
200
+ ];
201
+
202
+ const data = await aggregate( pipeline );
203
+
204
+ if ( !data[0]?.currentPeriod?.length ) {
205
+ return res.sendError( 'No data found', 204 );
206
+ }
207
+
208
+ if ( !data[0]?.last7Days?.length ) {
209
+ const resData = {
210
+ ...data[0].currentPeriod[0],
211
+ };
212
+ return res.sendSuccess( resData );
213
+ }
214
+
215
+ const firstDate = dayjs.utc( req.body.startDate );
216
+ const secondDate = dayjs.utc( req.body.endDate );
217
+
218
+ const daysDifference = secondDate.diff( firstDate, 'day' ) + 1;
219
+
220
+ const currentRangeData = {
221
+ questionFlag: Math.round( data[0].currentPeriod[0].questionFlag / daysDifference ),
222
+ timeFlag: Math.round( data[0].currentPeriod[0].timeFlag / daysDifference ),
223
+ detectionFlags: Math.round( data[0].currentPeriod[0].detectionFlags / daysDifference ),
224
+ };
225
+
226
+ const last7RangeData = {
227
+ questionFlag: Math.round( data[0].currentPeriod[0].questionFlag / 7 ),
228
+ timeFlag: Math.round( data[0].currentPeriod[0].timeFlag / 7 ),
229
+ detectionFlags: Math.round( data[0].currentPeriod[0].detectionFlags / 7 ),
230
+ };
231
+
232
+ function calculatePercentage( currentPeriod, last7Days ) {
233
+ // const current = currentPeriod[0];
234
+ // const last7 = last7Days[0];
235
+
236
+ const calculateDiffPercentage = ( currentValue, last7Value ) => {
237
+ if ( last7Value === 0 ) {
238
+ return currentValue === 0 ? 0 : ( currentValue > 0 ? 100 : -100 );
239
+ }
240
+ return Math.round( ( ( currentValue - last7Value ) / last7Value ) * 100 );
241
+ };
242
+
243
+ const percentageQuestionFlag = calculateDiffPercentage( currentPeriod.questionFlag, last7Days.questionFlag );
244
+ const percentageTimeFlag = calculateDiffPercentage( currentPeriod.timeFlag, last7Days.timeFlag );
245
+ const percentageDetectionFlags = calculateDiffPercentage( currentPeriod.detectionFlags, last7Days.detectionFlags );
246
+
247
+ return {
248
+ percentageQuestionFlag,
249
+ percentageTimeFlag,
250
+ percentageDetectionFlags,
251
+ };
252
+ }
253
+
254
+ const percentageData = calculatePercentage( currentRangeData, last7RangeData );
255
+
256
+ const resData = {
257
+ ...data[0].currentPeriod[0],
258
+ ...percentageData,
259
+ };
260
+
261
+ return res.sendSuccess( resData );
262
+ } catch ( error ) {
263
+ logger.error( { error: error, message: req.body, function: 'overallFlagMetrics' } );
264
+ return res.sendError( { error: error }, 500 );
265
+ }
266
+ };
267
+
268
+ export const checklistFlagsTable = async ( req, res ) => {
269
+ try {
270
+ const matchStage = {
271
+ $match: {
272
+ $and: [
273
+ { client_id: req.body.clientId },
274
+ {
275
+ store_id: {
276
+ $in: req.body.stores,
277
+ },
278
+ },
279
+ {
280
+ date_iso: {
281
+ $gte: new Date( req.body.startDate ),
282
+ },
283
+ },
284
+ {
285
+ date_iso: {
286
+ $lte: new Date( req.body.endDate ),
287
+ },
288
+ },
289
+ {
290
+ $or: [
291
+ { timeFlag: { $gt: 0 } },
292
+ { questionFlag: { $gt: 0 } },
293
+ { mobileDetectionFlag: { $gt: 0 } },
294
+ { storeOpenCloseFlag: { $gt: 0 } },
295
+ { uniformDetectionFlag: { $gt: 0 } },
296
+ ],
297
+ },
298
+ ],
299
+ },
300
+ };
301
+
302
+ const pipeline = [
303
+ matchStage,
304
+ {
305
+ $project: {
306
+ sourceCheckList_id: 1,
307
+ checkListName: 1,
308
+ storeCount: 1,
309
+ store_id: 1,
310
+ checkListType: 1,
311
+ flags: [
312
+ {
313
+ flagType: 'Not Submitted',
314
+ flagValue: '$timeFlag',
315
+ },
316
+ {
317
+ flagType: 'Question',
318
+ flagValue: '$questionFlag',
319
+ },
320
+ {
321
+ flagType: 'Mobile Detection',
322
+ flagValue: '$mobileDetectionFlag',
323
+ },
324
+ {
325
+ flagType: 'Store Open/Close',
326
+ flagValue: '$storeOpenCloseFlag',
327
+ },
328
+ {
329
+ flagType: 'Uniform Detection',
330
+ flagValue: '$uniformDetectionFlag',
331
+ },
332
+ ],
333
+ },
334
+ },
335
+ {
336
+ $unwind: '$flags',
337
+ },
338
+ {
339
+ $match: {
340
+ 'flags.flagValue': { $gt: 0 },
341
+ },
342
+ },
343
+ {
344
+ $group: {
345
+ _id: {
346
+ sourceCheckList_id: '$sourceCheckList_id',
347
+ flagType: '$flags.flagType',
348
+ },
349
+ checkListName: { $last: '$checkListName' },
350
+ checkListType: { $last: '$checkListType' },
351
+ checkListChar: {
352
+ $last: {
353
+ $substr: [ '$checkListName', 0, 2 ],
354
+ },
355
+ },
356
+ storeCount: { $max: '$storeCount' },
357
+ flaggedChecklist: { $sum: 1 },
358
+ flagType: { $last: '$flags.flagType' },
359
+ uniqueStores: { $addToSet: '$store_id' },
360
+ },
361
+ },
362
+ {
363
+ $project: {
364
+ _id: 1,
365
+ checkListName: 1,
366
+ checkListChar: 1,
367
+ checkListType: 1,
368
+ storeCount: 1,
369
+ flaggedChecklist: 1,
370
+ flagType: 1,
371
+ flagedStores: { $size: '$uniqueStores' },
372
+ },
373
+ },
374
+ ];
375
+
376
+ if ( req.body?.sortColumn && req.body?.sortBy ) {
377
+ pipeline.push(
378
+ {
379
+ $addFields: {
380
+ sortField: {
381
+ $toLower: `$${req.body.sortColumn}`,
382
+ },
383
+ },
384
+ },
385
+ {
386
+ $sort: {
387
+ [req.body.sortColumn]: req.body.sortBy,
388
+ },
389
+ },
390
+ );
391
+ }
392
+
393
+ pipeline.push(
394
+ {
395
+ $project: {
396
+ sortField: 0,
397
+ },
398
+ },
399
+
400
+ );
401
+
402
+ const facetStage = {
403
+ $facet: {
404
+ data: [
405
+ {
406
+ $skip: ( ( req.body.offset - 1 ) * req.body.limit ),
407
+ },
408
+ {
409
+ $limit: ( req.body.limit ),
410
+ },
411
+ ],
412
+ pageInfo: [
413
+ {
414
+ $count: 'count',
415
+ },
416
+ ],
417
+ },
418
+ };
419
+
420
+
421
+ pipeline.push( facetStage );
422
+
423
+ pipeline.push( {
424
+ $unwind: {
425
+ path: '$pageInfo',
426
+ },
427
+ } );
428
+
429
+ if ( req.body.isExport ) {
430
+ facetStage.$facet.data = [];
431
+ }
432
+
433
+
434
+ const data = await aggregate( pipeline );
435
+
436
+ if ( !data[0] ) {
437
+ return res.sendError( 'No data found', 204 );
438
+ }
439
+
440
+ if ( req.body.isExport ) {
441
+ const exportResult = [];
442
+ for ( let checklist of data[0].data ) {
443
+ exportResult.push( {
444
+ 'Checklist Name': checklist.checkListName ||'',
445
+ 'Flag Type': checklist.flagType||'',
446
+ 'Assigned Stores': checklist.storeCount||'',
447
+ 'Fragged Stores': checklist.flagedStores || '',
448
+ 'Flagged Count': checklist.flaggedChecklist||'',
449
+ } );
450
+ }
451
+ await download( exportResult, res );
452
+ return;
453
+ }
454
+
455
+ return res.sendSuccess( data[0] );
456
+ } catch ( error ) {
457
+ logger.error( { error: error, function: 'subscribedStoreList' } );
458
+ return res.sendError( error, 500 );
459
+ }
460
+ };