tango-app-api-trax 3.9.53 → 3.9.54

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-trax",
3
- "version": "3.9.53",
3
+ "version": "3.9.54",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "path": "^0.12.7",
31
31
  "puppeteer": "^24.39.1",
32
32
  "swagger-ui-express": "^5.0.1",
33
- "tango-api-schema": "^2.6.23",
33
+ "tango-api-schema": "^2.6.30",
34
34
  "tango-app-api-middleware": "^3.5.2",
35
35
  "url": "^0.11.4",
36
36
  "winston": "^3.13.1",
@@ -2150,6 +2150,14 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2150
2150
  if ( question.compliance ) {
2151
2151
  const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
2152
2152
  let score = scores.length ? Math.max( ...scores ) : 0;
2153
+ // Single-choice questions answered 'NA' / 'N/A' (case-insensitive) score 0,
2154
+ // matching the visit-checklist PDF and dashboard compliance calculation.
2155
+ if ( question.answerType === 'multiplechoicesingle' ) {
2156
+ const naAnswer = ( question.userAnswer?.[0]?.answer || '' ).toString().trim().toLowerCase();
2157
+ if ( naAnswer === 'na' || naAnswer === 'n/a' ) {
2158
+ score = 0;
2159
+ }
2160
+ }
2153
2161
  requestData.userComplianceCount += score;
2154
2162
  }
2155
2163
  }
@@ -326,6 +326,83 @@ export const checklistPerformance = async ( req, res ) => {
326
326
  // },
327
327
  // } );
328
328
 
329
+ // Recompute compliance per submission straight from questionAnswers so the
330
+ // dashboard % matches the visit-checklist PDF: a 'multiplechoicesingle'
331
+ // question answered 'NA'/'N/A' (case-insensitive) is excluded from BOTH the
332
+ // score (numerator) and the max (denominator). Non-compliance questions and
333
+ // non-NA questions keep the existing max/score formula.
334
+ findQuery.push( {
335
+ $addFields: {
336
+ naAwareCompliance: {
337
+ $reduce: {
338
+ input: { $ifNull: [ '$questionAnswers', [] ] },
339
+ initialValue: { score: 0, max: 0 },
340
+ in: {
341
+ $reduce: {
342
+ input: { $ifNull: [ '$$this.questions', [] ] },
343
+ initialValue: '$$value',
344
+ in: {
345
+ $let: {
346
+ vars: {
347
+ // Count this question's score/max unless it is a single-choice
348
+ // question answered NA/N/A, or a non-compliance question.
349
+ countIt: {
350
+ $and: [
351
+ { $toBool: { $ifNull: [ '$$this.compliance', false ] } },
352
+ { $not: [ {
353
+ $and: [
354
+ { $eq: [ '$$this.answerType', 'multiplechoicesingle' ] },
355
+ { $in: [
356
+ { $toLower: { $trim: { input: { $convert: {
357
+ input: { $arrayElemAt: [ { $ifNull: [ '$$this.userAnswer.answer', [] ] }, 0 ] },
358
+ to: 'string', onError: '', onNull: '',
359
+ } } } } },
360
+ [ 'na', 'n/a' ],
361
+ ] },
362
+ ],
363
+ } ] },
364
+ ],
365
+ },
366
+ },
367
+ in: {
368
+ score: {
369
+ $add: [
370
+ '$$value.score',
371
+ { $cond: [
372
+ '$$countIt',
373
+ { $ifNull: [ { $max: { $map: {
374
+ input: { $ifNull: [ '$$this.userAnswer', [] ] },
375
+ as: 'u',
376
+ in: { $ifNull: [ '$$u.complianceScore', 0 ] },
377
+ } } }, 0 ] },
378
+ 0,
379
+ ] },
380
+ ],
381
+ },
382
+ max: {
383
+ $add: [
384
+ '$$value.max',
385
+ { $cond: [
386
+ '$$countIt',
387
+ { $ifNull: [ { $max: { $map: {
388
+ input: { $ifNull: [ '$$this.answers', [] ] },
389
+ as: 'o',
390
+ in: { $ifNull: [ '$$o.complianceScore', { $max: [ { $ifNull: [ '$$o.matchedCount', 0 ] }, { $ifNull: [ '$$o.notMatchedCount', 0 ] } ] } ] },
391
+ } } }, 0 ] },
392
+ 0,
393
+ ] },
394
+ ],
395
+ },
396
+ },
397
+ },
398
+ },
399
+ },
400
+ },
401
+ },
402
+ },
403
+ },
404
+ } );
405
+
329
406
  findQuery.push( {
330
407
  $group: {
331
408
  _id: '$sourceCheckList_id',
@@ -344,16 +421,24 @@ export const checklistPerformance = async ( req, res ) => {
344
421
  timeFlag: { $sum: '$timeFlag' },
345
422
  questionFlagCount: { $sum: '$questionFlag' },
346
423
  runAIFlagCount: { $sum: '$runAIFlag' },
347
- userComplianceCountTotal: {
424
+ userComplianceScoreTotal: {
348
425
  $sum: {
349
426
  $cond: [
350
427
  { $eq: [ '$checklistStatus', 'submit' ] },
351
- { $ifNull: [ '$userComplianceCount', 0 ] },
428
+ { $ifNull: [ '$naAwareCompliance.score', 0 ] },
429
+ 0,
430
+ ],
431
+ },
432
+ },
433
+ complianceMaxTotal: {
434
+ $sum: {
435
+ $cond: [
436
+ { $eq: [ '$checklistStatus', 'submit' ] },
437
+ { $ifNull: [ '$naAwareCompliance.max', 0 ] },
352
438
  0,
353
439
  ],
354
440
  },
355
441
  },
356
- complianceCount: { $first: '$complianceCount' },
357
442
  checkListType: { $last: '$checkListType' },
358
443
  redo: { $sum: { $cond: [ { $eq: [ '$redoStatus', true ] }, 1, 0 ] } },
359
444
  task: {
@@ -414,39 +499,26 @@ export const checklistPerformance = async ( req, res ) => {
414
499
  redo: 1,
415
500
  task: 1,
416
501
  questionCompliance: {
417
- $let: {
418
- vars: {
419
- divisor: {
420
- $cond: [
421
- { $gt: [ '$submittedChecklist', 1 ] },
422
- { $multiply: [ { $ifNull: [ '$complianceCount', 0 ] }, '$submittedChecklist' ] },
423
- { $ifNull: [ '$complianceCount', 0 ] },
424
- ],
425
- },
426
- },
427
- in: {
428
- $cond: [
429
- { $gt: [ '$$divisor', 0 ] },
502
+ $cond: [
503
+ { $gt: [ '$complianceMaxTotal', 0 ] },
504
+ {
505
+ $max: [
506
+ 0,
430
507
  {
431
- $max: [
432
- 0,
508
+ $round: [
433
509
  {
434
- $round: [
435
- {
436
- $multiply: [
437
- { $divide: [ '$userComplianceCountTotal', '$$divisor' ] },
438
- 100,
439
- ],
440
- },
441
- 0,
510
+ $multiply: [
511
+ { $divide: [ '$userComplianceScoreTotal', '$complianceMaxTotal' ] },
512
+ 100,
442
513
  ],
443
514
  },
515
+ 0,
444
516
  ],
445
517
  },
446
- 0,
447
518
  ],
448
519
  },
449
- },
520
+ 0,
521
+ ],
450
522
  },
451
523
  },
452
524
  } );