tango-app-api-trax 3.9.57 → 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.57",
3
+ "version": "3.9.58",
4
4
  "description": "Trax",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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,32 @@ 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
+
2153
2193
  // Single-choice questions answered 'NA' / 'N/A' (case-insensitive) contribute 0;
2154
2194
  // don't count the selected answer's compliance score for that question.
2155
2195
  if ( question.answerType === 'multiplechoicesingle' ) {