tango-app-api-trax 3.9.56 → 3.9.58

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.56",
3
+ "version": "3.9.58",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -2834,6 +2834,14 @@ export async function countUpdateRunAI( req, res ) {
2834
2834
  const scores = ( question.userAnswer || [] ).map( ( ua ) => ua.complianceScore ?? 0 );
2835
2835
  score = scores.length ? Math.max( ...scores ) : 0;
2836
2836
  }
2837
+ // Single-choice questions answered 'NA' / 'N/A' (case-insensitive) contribute 0;
2838
+ // don't count the selected answer's compliance score for that question.
2839
+ if ( question.answerType === 'multiplechoicesingle' ) {
2840
+ const naAnswer = ( question.userAnswer?.[0]?.answer || '' ).toString().trim().toLowerCase();
2841
+ if ( naAnswer === 'na' || naAnswer === 'n/a' ) {
2842
+ score = 0;
2843
+ }
2844
+ }
2837
2845
  userComplianceTotal += score;
2838
2846
  }
2839
2847
  } );
@@ -2141,6 +2141,22 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2141
2141
 
2142
2142
  if ( requestData.submittype == 'submit' ) {
2143
2143
  requestData.userComplianceCount = 0;
2144
+
2145
+ // Pull every "Matched/Not Matched" verdict out of a runAIData array, tolerating both shapes:
2146
+ // 1) [ { answerImage, results: [ { featureName, value } ] } ] (verdict nested under results)
2147
+ // 2) [ { featureName, value } ] (flat verdict)
2148
+ const extractMatchValues = ( runAIData ) => {
2149
+ if ( !Array.isArray( runAIData ) ) return [];
2150
+ const values = [];
2151
+ runAIData.forEach( ( entry ) => {
2152
+ const results = Array.isArray( entry?.results ) ? entry.results : [ entry ];
2153
+ results.forEach( ( r ) => {
2154
+ if ( r?.featureName === 'Matched/Not Matched' ) values.push( r?.value );
2155
+ } );
2156
+ } );
2157
+ return values;
2158
+ };
2159
+
2144
2160
  for ( let section of sectionFormat ) {
2145
2161
  for ( let question of section.questions ) {
2146
2162
  let mustValidate = !question.linkType || ( question.linkType && question.linkquestionenabled );
@@ -2148,8 +2164,40 @@ export async function sopMobilechecklistMultiSectionFormatterv2( req, res, next
2148
2164
  return res.sendError( 'Please Fill All Fields', 400 );
2149
2165
  }
2150
2166
  if ( question.compliance ) {
2151
- const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
2152
- let score = scores.length ? Math.max( ...scores ) : 0;
2167
+ let score = 0;
2168
+
2169
+ // image / image/video: when runAI verdicts are already present on the userAnswer,
2170
+ // derive compliance from them (every "Matched/Not Matched" must be True to count as
2171
+ // matched, else not matched), mirroring countUpdateRunAI.
2172
+ const aiAnswers = question.answerType === 'image/video' ?
2173
+ ( question.userAnswer || [] ).filter( ( ua ) => ua?.answerType === 'image' ) :
2174
+ ( question.userAnswer || [] );
2175
+ const hasRunAIData = [ 'image', 'image/video' ].includes( question.answerType ) &&
2176
+ aiAnswers.some( ( ua ) => Array.isArray( ua?.runAIData ) && ua.runAIData.length );
2177
+
2178
+ if ( hasRunAIData ) {
2179
+ const matchValues = aiAnswers.flatMap( ( ua ) => extractMatchValues( ua?.runAIData ) );
2180
+ const allMatched = matchValues.length > 0 && matchValues.every( ( v ) => v === 'True' || v === true );
2181
+ score = allMatched ?
2182
+ ( question.answers?.[0]?.matchedCount ?? 0 ) :
2183
+ ( question.answers?.[0]?.notMatchedCount ?? 0 );
2184
+ // Persist the derived score onto each userAnswer so PDF/dashboard reads stay consistent.
2185
+ ( question.userAnswer || [] ).forEach( ( ua ) => {
2186
+ ua.complianceScore = score;
2187
+ } );
2188
+ } else {
2189
+ const scores = ( question.userAnswer || [] ).map( ( ua ) => ua?.complianceScore ?? 0 );
2190
+ score = scores.length ? Math.max( ...scores ) : 0;
2191
+ }
2192
+
2193
+ // Single-choice questions answered 'NA' / 'N/A' (case-insensitive) contribute 0;
2194
+ // don't count the selected answer's compliance score for that question.
2195
+ if ( question.answerType === 'multiplechoicesingle' ) {
2196
+ const naAnswer = ( question.userAnswer?.[0]?.answer || '' ).toString().trim().toLowerCase();
2197
+ if ( naAnswer === 'na' || naAnswer === 'n/a' ) {
2198
+ score = 0;
2199
+ }
2200
+ }
2153
2201
  requestData.userComplianceCount += score;
2154
2202
  }
2155
2203
  }