tango-app-api-trax 3.9.52 → 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.
|
|
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.
|
|
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
|
-
|
|
424
|
+
userComplianceScoreTotal: {
|
|
348
425
|
$sum: {
|
|
349
426
|
$cond: [
|
|
350
427
|
{ $eq: [ '$checklistStatus', 'submit' ] },
|
|
351
|
-
{ $ifNull: [ '$
|
|
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
|
-
$
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
$
|
|
432
|
-
0,
|
|
508
|
+
$round: [
|
|
433
509
|
{
|
|
434
|
-
$
|
|
435
|
-
{
|
|
436
|
-
|
|
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
|
} );
|
|
@@ -552,11 +552,21 @@ function mapSectionsFromQuestionAnswer( questionAnswer ) {
|
|
|
552
552
|
}
|
|
553
553
|
|
|
554
554
|
|
|
555
|
-
|
|
556
|
-
|
|
555
|
+
let max = q.compliance ? Math.max( ...q?.answers.map( ( o ) => o?.complianceScore ?? Math.max( o?.matchedCount ?? 0, o?.notMatchedCount ?? 0 ) ) ) : 0;
|
|
557
556
|
|
|
558
557
|
let score = q.compliance ? Math.max( ...q?.userAnswer?.map( ( o ) => o?.complianceScore ?? 0 ) ) : 0;
|
|
559
558
|
|
|
559
|
+
// Single-choice questions answered 'NA' / 'N/A' (case-insensitive) are
|
|
560
|
+
// excluded from scoring entirely: both score and max are 0, so they add
|
|
561
|
+
// nothing to the section/total numerator or denominator.
|
|
562
|
+
if ( q.answerType === 'multiplechoicesingle' ) {
|
|
563
|
+
const naAnswer = ( ua?.answer || '' ).toString().trim().toLowerCase();
|
|
564
|
+
if ( naAnswer === 'na' || naAnswer === 'n/a' ) {
|
|
565
|
+
score = 0;
|
|
566
|
+
max = 0;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
560
570
|
|
|
561
571
|
if ( q.compliance && ( q.answerType === 'image' || q.answerType === 'image/video' ) ) {
|
|
562
572
|
// image/video questions mix image + video entries in userAnswer, so only score the images.
|