Spaces:
Sleeping
Sleeping
Update services/difyService.ts
Browse files- services/difyService.ts +841 -261
services/difyService.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
|
2 |
import { API_URL, API_TOKEN, API_USER } from '../constants';
|
3 |
import { ApiInput, ApiResponseOutput, ProcessedResult, QaSectionResult, DetailedQaReport } from '../types';
|
4 |
|
@@ -67,12 +66,19 @@ const parseSection = (sectionText: string): QaSectionResult => {
|
|
67 |
}
|
68 |
console.log('Grade match result:', gradeMatch, 'Final grade:', grade);
|
69 |
|
70 |
-
// ROBUST PASS EXTRACTION - handles multiple formats
|
71 |
let pass = false;
|
72 |
let passMatch = null;
|
73 |
|
74 |
-
// Try various pass patterns in order of specificity
|
75 |
const passPatterns = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
/-\s*\*\*Pass:\*\*\s*(.*)/i, // - **Pass:** true
|
77 |
/•\s*\*\*Pass:\*\*\s*(.*)/i, // • **Pass:** true
|
78 |
/\*\s*\*\*Pass:\*\*\s*(.*)/i, // * **Pass:** true
|
@@ -89,11 +95,11 @@ const parseSection = (sectionText: string): QaSectionResult => {
|
|
89 |
passMatch = sectionText.match(pattern);
|
90 |
if (passMatch) {
|
91 |
const passValue = passMatch[1].toLowerCase().trim();
|
92 |
-
pass = passValue.includes('
|
|
|
93 |
passValue.includes('✅') ||
|
94 |
passValue === 'yes' ||
|
95 |
-
passValue === 'passed'
|
96 |
-
passValue === 'pass';
|
97 |
break;
|
98 |
}
|
99 |
}
|
@@ -139,6 +145,22 @@ const parseSection = (sectionText: string): QaSectionResult => {
|
|
139 |
}
|
140 |
}
|
141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
// ROBUST ANALYSIS/CORRECTED CONTENT EXTRACTION - handles multiple formats
|
143 |
let corrected = 'Content analysis not available.';
|
144 |
let contentMatch = null;
|
@@ -179,6 +201,7 @@ const parseSection = (sectionText: string): QaSectionResult => {
|
|
179 |
corrected = corrected.replace(/^#\s*/, '').replace(/###\s*\*\*[^*]+\*\*/, '').trim();
|
180 |
console.log('Content match result:', contentMatch, 'Final corrected:', corrected.substring(0, 50));
|
181 |
|
|
|
182 |
return { grade, pass, errors, corrected };
|
183 |
};
|
184 |
|
@@ -400,132 +423,266 @@ const parseEnhancedSection = (sectionBlock: string): QaSectionResult => {
|
|
400 |
* Enhanced overall section parsing that captures ALL QA Guard content
|
401 |
*/
|
402 |
const parseEnhancedOverallSection = (sectionBlock: string): { grade: string; pass: boolean; primaryIssue: string; detailedAssessment?: string; keyStrengths?: string[]; recommendations?: string[]; explanations?: string; rawContent?: string } => {
|
403 |
-
|
|
|
|
|
404 |
let grade = 'N/A';
|
405 |
-
let
|
|
|
|
|
|
|
|
|
|
|
406 |
|
|
|
|
|
407 |
const overallGradePatterns = [
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
/
|
415 |
-
/
|
416 |
-
/
|
417 |
-
/(\d
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
];
|
419 |
|
|
|
420 |
for (const pattern of overallGradePatterns) {
|
421 |
-
|
422 |
-
if (
|
423 |
-
grade =
|
|
|
424 |
break;
|
425 |
}
|
426 |
}
|
427 |
-
console.log('Overall grade match:', gradeMatch, 'Final grade:', grade);
|
428 |
-
|
429 |
-
// ROBUST OVERALL PASS EXTRACTION - handles multiple formats
|
430 |
-
let pass = false;
|
431 |
-
let passMatch = null;
|
432 |
|
|
|
|
|
433 |
const overallPassPatterns = [
|
434 |
-
//
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
//
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
//
|
450 |
-
/Pass
|
451 |
-
/
|
452 |
-
/
|
453 |
-
/(
|
454 |
-
|
455 |
-
// Handle capitalized boolean values
|
456 |
-
/Pass:?:\s*(True|False|TRUE|FALSE)/im,
|
457 |
-
/Overall\s*Pass:?:\s*(True|False|TRUE|FALSE)/im
|
458 |
];
|
459 |
|
|
|
460 |
for (const pattern of overallPassPatterns) {
|
461 |
-
|
462 |
-
if (
|
463 |
-
|
464 |
-
|
465 |
-
passValue.includes('✅') ||
|
466 |
-
passValue === 'yes' ||
|
467 |
-
passValue === 'passed' ||
|
468 |
-
passValue === 'pass';
|
469 |
break;
|
470 |
}
|
471 |
}
|
472 |
-
console.log('Overall pass match:', passMatch, 'Final pass:', pass);
|
473 |
|
474 |
-
//
|
475 |
-
|
476 |
-
const
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
481 |
|
482 |
-
let
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
} else if (errorsMatch) {
|
490 |
-
const errorText = errorsMatch[1].trim();
|
491 |
-
if (errorText !== '[]' && errorText !== '') {
|
492 |
-
primaryIssue = `Errors found: ${errorText}`;
|
493 |
}
|
494 |
}
|
495 |
-
if (totalSectionsMatch) {
|
496 |
-
primaryIssue = `${primaryIssue} | Total Sections Passing: ${totalSectionsMatch[1].trim()}`;
|
497 |
-
}
|
498 |
|
499 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
|
501 |
-
|
502 |
-
const
|
503 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
504 |
|
505 |
// Extract key strengths
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
511 |
|
512 |
// Extract recommendations
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
518 |
|
519 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
520 |
|
521 |
-
|
522 |
-
|
523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
524 |
primaryIssue,
|
525 |
-
detailedAssessment:
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
rawContent: sectionBlock
|
530 |
};
|
531 |
};
|
@@ -571,184 +728,607 @@ const parseNewQaReport = (qaText: string): { detailedQaReport: DetailedQaReport,
|
|
571 |
return { detailedQaReport: defaultReport, overallPass: false, overallGrade: 'N/A' };
|
572 |
}
|
573 |
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
|
579 |
-
//
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
593 |
} else {
|
594 |
-
sections
|
595 |
-
console.log('
|
|
|
|
|
|
|
|
|
|
|
|
|
596 |
}
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
|
|
601 |
|
602 |
-
|
603 |
-
|
604 |
-
const
|
|
|
|
|
605 |
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
|
|
|
|
|
|
|
|
|
|
610 |
}
|
611 |
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
h1: parsedData.h1 || { ...defaultSection, errors: ['H1 section not found'] },
|
659 |
-
copy: parsedData.copy || { ...defaultSection, errors: ['Copy section not found'] },
|
660 |
-
overall: parsedData.overall || { grade: 'N/A', pass: false, primaryIssue: 'Overall section not found' },
|
661 |
-
additionalSections: Object.keys(additionalSections).length > 0 ? additionalSections : undefined,
|
662 |
-
completeRawReport: qaText
|
663 |
-
};
|
664 |
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
670 |
}
|
671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
672 |
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
|
|
|
|
680 |
let grade = 'N/A';
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
685 |
}
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
693 |
}
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
703 |
} else {
|
704 |
-
//
|
705 |
-
|
706 |
-
finalReport.meta.pass &&
|
707 |
-
finalReport.h1.pass &&
|
708 |
-
finalReport.copy.pass;
|
709 |
-
|
710 |
-
// Calculate average grade if all sections have numeric grades
|
711 |
-
let averageGrade = 'N/A';
|
712 |
-
const grades = [finalReport.title.grade, finalReport.meta.grade, finalReport.h1.grade, finalReport.copy.grade];
|
713 |
-
const numericGrades = grades.filter(g => g !== 'N/A' && g !== undefined)
|
714 |
-
.map(g => {
|
715 |
-
const match = String(g).match(/(\d+(?:\.\d+)?)/);
|
716 |
-
return match ? parseFloat(match[1]) : null;
|
717 |
-
})
|
718 |
-
.filter(g => g !== null) as number[];
|
719 |
-
|
720 |
-
if (numericGrades.length === 4) {
|
721 |
-
const avg = numericGrades.reduce((sum, grade) => sum + grade, 0) / numericGrades.length;
|
722 |
-
averageGrade = `${avg.toFixed(2)}/100`;
|
723 |
-
}
|
724 |
-
|
725 |
-
parsedData.overall = {
|
726 |
-
grade: averageGrade,
|
727 |
-
pass: allSectionsPassed,
|
728 |
-
primaryIssue: allSectionsPassed ? 'All sections passed successfully.' : 'One or more sections failed.',
|
729 |
-
rawContent: cleanedQaText
|
730 |
-
};
|
731 |
-
|
732 |
-
console.log('Calculated overall from individual sections - pass:', allSectionsPassed, 'grade:', averageGrade);
|
733 |
}
|
734 |
-
|
735 |
-
|
736 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
737 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
738 |
|
739 |
-
|
740 |
-
|
741 |
-
|
|
|
|
|
742 |
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
751 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
752 |
};
|
753 |
|
754 |
|
|
|
|
|
1 |
import { API_URL, API_TOKEN, API_USER } from '../constants';
|
2 |
import { ApiInput, ApiResponseOutput, ProcessedResult, QaSectionResult, DetailedQaReport } from '../types';
|
3 |
|
|
|
66 |
}
|
67 |
console.log('Grade match result:', gradeMatch, 'Final grade:', grade);
|
68 |
|
69 |
+
// ROBUST PASS EXTRACTION - handles multiple formats including the actual QA Guard format
|
70 |
let pass = false;
|
71 |
let passMatch = null;
|
72 |
|
73 |
+
// Try various pass patterns in order of specificity - FIXED TO HANDLE ACTUAL QA GUARD FORMAT
|
74 |
const passPatterns = [
|
75 |
+
// First check for the actual QA Guard format: "### **TITLE: PASS** ✅"
|
76 |
+
/###\s*\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*\s*(✅|❌)/i,
|
77 |
+
/###\s*\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*/i,
|
78 |
+
/\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*\s*(✅|❌)/i,
|
79 |
+
/\*\*[^:]+:\s*(PASS|FAIL)\s*\*\*/i,
|
80 |
+
|
81 |
+
// Then check for traditional patterns
|
82 |
/-\s*\*\*Pass:\*\*\s*(.*)/i, // - **Pass:** true
|
83 |
/•\s*\*\*Pass:\*\*\s*(.*)/i, // • **Pass:** true
|
84 |
/\*\s*\*\*Pass:\*\*\s*(.*)/i, // * **Pass:** true
|
|
|
95 |
passMatch = sectionText.match(pattern);
|
96 |
if (passMatch) {
|
97 |
const passValue = passMatch[1].toLowerCase().trim();
|
98 |
+
pass = passValue.includes('pass') ||
|
99 |
+
passValue.includes('true') ||
|
100 |
passValue.includes('✅') ||
|
101 |
passValue === 'yes' ||
|
102 |
+
passValue === 'passed';
|
|
|
103 |
break;
|
104 |
}
|
105 |
}
|
|
|
145 |
}
|
146 |
}
|
147 |
|
148 |
+
// ENHANCED LOGIC: If we have a grade of 100/100 and no errors, but pass is still false,
|
149 |
+
// we should override the pass status based on the grade and errors
|
150 |
+
if (grade === '100/100' && (!errors || errors.length === 0 || errors[0] === 'No errors reported.')) {
|
151 |
+
console.log('Overriding pass status: Grade is 100/100 and no errors, setting pass to true');
|
152 |
+
pass = true;
|
153 |
+
}
|
154 |
+
|
155 |
+
// Additional logic: If grade is high (80+) and no errors, likely a pass
|
156 |
+
if (grade !== 'N/A' && grade !== '0/100') {
|
157 |
+
const gradeNum = parseInt(grade.split('/')[0]);
|
158 |
+
if (gradeNum >= 80 && (!errors || errors.length === 0 || errors[0] === 'No errors reported.')) {
|
159 |
+
console.log(`Overriding pass status: Grade is ${grade} (${gradeNum}/100) and no errors, setting pass to true`);
|
160 |
+
pass = true;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
// ROBUST ANALYSIS/CORRECTED CONTENT EXTRACTION - handles multiple formats
|
165 |
let corrected = 'Content analysis not available.';
|
166 |
let contentMatch = null;
|
|
|
201 |
corrected = corrected.replace(/^#\s*/, '').replace(/###\s*\*\*[^*]+\*\*/, '').trim();
|
202 |
console.log('Content match result:', contentMatch, 'Final corrected:', corrected.substring(0, 50));
|
203 |
|
204 |
+
console.log('Final section result - Grade:', grade, 'Pass:', pass, 'Errors:', errors);
|
205 |
return { grade, pass, errors, corrected };
|
206 |
};
|
207 |
|
|
|
423 |
* Enhanced overall section parsing that captures ALL QA Guard content
|
424 |
*/
|
425 |
const parseEnhancedOverallSection = (sectionBlock: string): { grade: string; pass: boolean; primaryIssue: string; detailedAssessment?: string; keyStrengths?: string[]; recommendations?: string[]; explanations?: string; rawContent?: string } => {
|
426 |
+
console.log('Parsing enhanced overall section with actual QA Guard format');
|
427 |
+
console.log('Overall section preview:', sectionBlock.substring(0, 300));
|
428 |
+
|
429 |
let grade = 'N/A';
|
430 |
+
let pass = false;
|
431 |
+
let primaryIssue = 'Overall assessment not available.';
|
432 |
+
let detailedAssessment = '';
|
433 |
+
let keyStrengths: string[] = [];
|
434 |
+
let recommendations: string[] = [];
|
435 |
+
let explanations = '';
|
436 |
|
437 |
+
// COMPREHENSIVE OVERALL GRADE PATTERN MATCHING
|
438 |
+
// Look for the actual QA Guard overall format: "Final Grade: 98.75/100"
|
439 |
const overallGradePatterns = [
|
440 |
+
/Final\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Final Grade: 98.75/100
|
441 |
+
/Overall\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Grade: 98.75/100
|
442 |
+
/Total\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Total Grade: 98.75/100
|
443 |
+
/Combined\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Combined Grade: 98.75/100
|
444 |
+
/Average\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Average Grade: 98.75/100
|
445 |
+
/Mean\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Mean Grade: 98.75/100
|
446 |
+
/Composite\s+Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Composite Grade: 98.75/100
|
447 |
+
/Final\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Final Score: 98.75/100
|
448 |
+
/Overall\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Score: 98.75/100
|
449 |
+
/Total\s+Score:\s*(\d+(?:\.\d+)?)\/100/i, // Total Score: 98.75/100
|
450 |
+
/Final\s+Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Final Rating: 98.75/100
|
451 |
+
/Overall\s+Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Rating: 98.75/100
|
452 |
+
/Final\s+Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Final Mark: 98.75/100
|
453 |
+
/Overall\s+Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Mark: 98.75/100
|
454 |
+
/Final\s+Points:\s*(\d+(?:\.\d+)?)\/100/i, // Final Points: 98.75/100
|
455 |
+
/Overall\s+Points:\s*(\d+(?:\.\d+)?)\/100/i, // Overall Points: 98.75/100
|
456 |
+
/-?\s*\*\*Final\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Grade:** 98.75/100
|
457 |
+
/-?\s*\*\*Overall\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Grade:** 98.75/100
|
458 |
+
/-?\s*\*\*Total\s+Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Total Grade:** 98.75/100
|
459 |
+
/-?\s*\*\*Final\s+Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Score:** 98.75/100
|
460 |
+
/-?\s*\*\*Overall\s+Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Score:** 98.75/100
|
461 |
+
/-?\s*\*\*Final\s+Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Final Rating:** 98.75/100
|
462 |
+
/-?\s*\*\*Overall\s+Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Overall Rating:** 98.75/100
|
463 |
+
/Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Grade: 98.75/100
|
464 |
+
/Score:\s*(\d+(?:\.\d+)?)\/100/i, // Score: 98.75/100
|
465 |
+
/Rating:\s*(\d+(?:\.\d+)?)\/100/i, // Rating: 98.75/100
|
466 |
+
/Mark:\s*(\d+(?:\.\d+)?)\/100/i, // Mark: 98.75/100
|
467 |
+
/Points:\s*(\d+(?:\.\d+)?)\/100/i, // Points: 98.75/100
|
468 |
];
|
469 |
|
470 |
+
let finalGradeMatch = null;
|
471 |
for (const pattern of overallGradePatterns) {
|
472 |
+
finalGradeMatch = sectionBlock.match(pattern);
|
473 |
+
if (finalGradeMatch) {
|
474 |
+
grade = `${finalGradeMatch[1]}/100`;
|
475 |
+
console.log('Found final grade:', grade);
|
476 |
break;
|
477 |
}
|
478 |
}
|
|
|
|
|
|
|
|
|
|
|
479 |
|
480 |
+
// COMPREHENSIVE OVERALL PASS STATUS PATTERN MATCHING
|
481 |
+
// Look for overall pass status: "Overall Pass: FALSE (due to META violation)"
|
482 |
const overallPassPatterns = [
|
483 |
+
/Overall\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Pass: FALSE (due to META violation)
|
484 |
+
/Final\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Pass: FALSE (due to META violation)
|
485 |
+
/Total\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Total Pass: FALSE (due to META violation)
|
486 |
+
/Combined\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Combined Pass: FALSE (due to META violation)
|
487 |
+
/Average\s+Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Average Pass: FALSE (due to META violation)
|
488 |
+
/Overall\s+Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Status: FALSE (due to META violation)
|
489 |
+
/Final\s+Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Status: FALSE (due to META violation)
|
490 |
+
/Overall\s+Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Result: FALSE (due to META violation)
|
491 |
+
/Final\s+Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Result: FALSE (due to META violation)
|
492 |
+
/Overall\s+Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Overall Assessment: FALSE (due to META violation)
|
493 |
+
/Final\s+Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Final Assessment: FALSE (due to META violation)
|
494 |
+
/-?\s*\*\*Overall\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Overall Pass:** FALSE (due to META violation)
|
495 |
+
/-?\s*\*\*Final\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Final Pass:** FALSE (due to META violation)
|
496 |
+
/-?\s*\*\*Total\s+Pass:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Total Pass:** FALSE (due to META violation)
|
497 |
+
/-?\s*\*\*Overall\s+Status:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Overall Status:** FALSE (due to META violation)
|
498 |
+
/-?\s*\*\*Final\s+Status:\*\*\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // - **Final Status:** FALSE (due to META violation)
|
499 |
+
/Pass:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Pass: FALSE (due to META violation)
|
500 |
+
/Status:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Status: FALSE (due to META violation)
|
501 |
+
/Result:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Result: FALSE (due to META violation)
|
502 |
+
/Assessment:\s*(TRUE|FALSE|PASS|FAIL)(?:\s*\([^)]+\))?/i, // Assessment: FALSE (due to META violation)
|
|
|
|
|
|
|
|
|
503 |
];
|
504 |
|
505 |
+
let overallPassMatch = null;
|
506 |
for (const pattern of overallPassPatterns) {
|
507 |
+
overallPassMatch = sectionBlock.match(pattern);
|
508 |
+
if (overallPassMatch) {
|
509 |
+
pass = overallPassMatch[1].toUpperCase() === 'TRUE' || overallPassMatch[1].toUpperCase() === 'PASS';
|
510 |
+
console.log('Found overall pass status:', pass);
|
|
|
|
|
|
|
|
|
511 |
break;
|
512 |
}
|
513 |
}
|
|
|
514 |
|
515 |
+
// COMPREHENSIVE PRIMARY ISSUE PATTERN MATCHING
|
516 |
+
// Look for primary issue in the pass status explanation
|
517 |
+
const issuePatterns = [
|
518 |
+
/Overall\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Overall Pass: FALSE (due to META violation)
|
519 |
+
/Final\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Final Pass: FALSE (due to META violation)
|
520 |
+
/Total\s+Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Total Pass: FALSE (due to META violation)
|
521 |
+
/Overall\s+Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Overall Status: FALSE (due to META violation)
|
522 |
+
/Final\s+Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Final Status: FALSE (due to META violation)
|
523 |
+
/Pass:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Pass: FALSE (due to META violation)
|
524 |
+
/Status:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Status: FALSE (due to META violation)
|
525 |
+
/Result:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Result: FALSE (due to META violation)
|
526 |
+
/Assessment:\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // Assessment: FALSE (due to META violation)
|
527 |
+
/-?\s*\*\*Overall\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Overall Pass:** FALSE (due to META violation)
|
528 |
+
/-?\s*\*\*Final\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Final Pass:** FALSE (due to META violation)
|
529 |
+
/-?\s*\*\*Total\s+Pass:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Total Pass:** FALSE (due to META violation)
|
530 |
+
/-?\s*\*\*Overall\s+Status:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Overall Status:** FALSE (due to META violation)
|
531 |
+
/-?\s*\*\*Final\s+Status:\*\*\s*(?:TRUE|FALSE|PASS|FAIL)\s*\(([^)]+)\)/i, // - **Final Status:** FALSE (due to META violation)
|
532 |
+
/-?\s*\*\*Primary\s+Issue:\*\*\s*([^\n]+)/i, // - **Primary Issue:** Some sections have violations
|
533 |
+
/-?\s*\*\*Main\s+Issue:\*\*\s*([^\n]+)/i, // - **Main Issue:** Some sections have violations
|
534 |
+
/-?\s*\*\*Key\s+Issue:\*\*\s*([^\n]+)/i, // - **Key Issue:** Some sections have violations
|
535 |
+
/-?\s*\*\*Issue:\*\*\s*([^\n]+)/i, // - **Issue:** Some sections have violations
|
536 |
+
/-?\s*\*\*Problem:\*\*\s*([^\n]+)/i, // - **Problem:** Some sections have violations
|
537 |
+
/-?\s*\*\*Concern:\*\*\s*([^\n]+)/i, // - **Concern:** Some sections have violations
|
538 |
+
/Primary\s+Issue:\s*([^\n]+)/i, // Primary Issue: Some sections have violations
|
539 |
+
/Main\s+Issue:\s*([^\n]+)/i, // Main Issue: Some sections have violations
|
540 |
+
/Key\s+Issue:\s*([^\n]+)/i, // Key Issue: Some sections have violations
|
541 |
+
/Issue:\s*([^\n]+)/i, // Issue: Some sections have violations
|
542 |
+
/Problem:\s*([^\n]+)/i, // Problem: Some sections have violations
|
543 |
+
/Concern:\s*([^\n]+)/i, // Concern: Some sections have violations
|
544 |
+
];
|
545 |
|
546 |
+
let issueMatch = null;
|
547 |
+
for (const pattern of issuePatterns) {
|
548 |
+
issueMatch = sectionBlock.match(pattern);
|
549 |
+
if (issueMatch) {
|
550 |
+
primaryIssue = issueMatch[1].trim();
|
551 |
+
console.log('Found primary issue:', primaryIssue);
|
552 |
+
break;
|
|
|
|
|
|
|
|
|
553 |
}
|
554 |
}
|
|
|
|
|
|
|
555 |
|
556 |
+
// COMPREHENSIVE DETAILED BREAKDOWN PATTERN MATCHING
|
557 |
+
// Look for detailed breakdown sections
|
558 |
+
const breakdownPatterns = [
|
559 |
+
/##\s+DETAILED\s+BREAKDOWN[^#]*/i, // ## DETAILED BREAKDOWN
|
560 |
+
/##\s+BREAKDOWN[^#]*/i, // ## BREAKDOWN
|
561 |
+
/##\s+ANALYSIS[^#]*/i, // ## ANALYSIS
|
562 |
+
/##\s+ASSESSMENT[^#]*/i, // ## ASSESSMENT
|
563 |
+
/##\s+EVALUATION[^#]*/i, // ## EVALUATION
|
564 |
+
/##\s+REVIEW[^#]*/i, // ## REVIEW
|
565 |
+
/##\s+SUMMARY[^#]*/i, // ## SUMMARY
|
566 |
+
/##\s+DETAILS[^#]*/i, // ## DETAILS
|
567 |
+
/##\s+EXPLANATION[^#]*/i, // ## EXPLANATION
|
568 |
+
/##\s+COMMENTS[^#]*/i, // ## COMMENTS
|
569 |
+
/##\s+NOTES[^#]*/i, // ## NOTES
|
570 |
+
/###\s+DETAILED\s+BREAKDOWN[^#]*/i, // ### DETAILED BREAKDOWN
|
571 |
+
/###\s+BREAKDOWN[^#]*/i, // ### BREAKDOWN
|
572 |
+
/###\s+ANALYSIS[^#]*/i, // ### ANALYSIS
|
573 |
+
/###\s+ASSESSMENT[^#]*/i, // ### ASSESSMENT
|
574 |
+
/###\s+EVALUATION[^#]*/i, // ### EVALUATION
|
575 |
+
/###\s+REVIEW[^#]*/i, // ### REVIEW
|
576 |
+
/###\s+SUMMARY[^#]*/i, // ### SUMMARY
|
577 |
+
/###\s+DETAILS[^#]*/i, // ### DETAILS
|
578 |
+
/###\s+EXPLANATION[^#]*/i, // ### EXPLANATION
|
579 |
+
/###\s+COMMENTS[^#]*/i, // ### COMMENTS
|
580 |
+
/###\s+NOTES[^#]*/i, // ### NOTES
|
581 |
+
];
|
582 |
|
583 |
+
let breakdownMatch = null;
|
584 |
+
for (const pattern of breakdownPatterns) {
|
585 |
+
breakdownMatch = sectionBlock.match(pattern);
|
586 |
+
if (breakdownMatch) {
|
587 |
+
detailedAssessment = breakdownMatch[0];
|
588 |
+
console.log('Found detailed breakdown');
|
589 |
+
break;
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
// ENHANCED KEY STRENGTHS AND RECOMMENDATIONS EXTRACTION
|
594 |
+
// Extract key strengths and recommendations from the overall assessment
|
595 |
+
const lowerSection = sectionBlock.toLowerCase();
|
596 |
|
597 |
// Extract key strengths
|
598 |
+
if (lowerSection.includes('compliance') && lowerSection.includes('requirements')) {
|
599 |
+
keyStrengths.push('Overall compliance with requirements');
|
600 |
+
}
|
601 |
+
if (grade !== 'N/A' && parseFloat(grade.split('/')[0]) >= 80) {
|
602 |
+
keyStrengths.push('High overall grade achieved');
|
603 |
+
}
|
604 |
+
if (lowerSection.includes('successful') || lowerSection.includes('approved')) {
|
605 |
+
keyStrengths.push('Overall assessment successful');
|
606 |
+
}
|
607 |
+
if (lowerSection.includes('meets') && lowerSection.includes('standards')) {
|
608 |
+
keyStrengths.push('Meets overall standards');
|
609 |
+
}
|
610 |
+
if (lowerSection.includes('satisfies') && lowerSection.includes('criteria')) {
|
611 |
+
keyStrengths.push('Satisfies overall criteria');
|
612 |
+
}
|
613 |
+
if (lowerSection.includes('valid') || lowerSection.includes('correct')) {
|
614 |
+
keyStrengths.push('Overall content validation passed');
|
615 |
+
}
|
616 |
|
617 |
// Extract recommendations
|
618 |
+
if (lowerSection.includes('violation') || lowerSection.includes('fail')) {
|
619 |
+
recommendations.push('Address identified violations');
|
620 |
+
}
|
621 |
+
if (lowerSection.includes('correction') || lowerSection.includes('fix')) {
|
622 |
+
recommendations.push('Implement suggested corrections');
|
623 |
+
}
|
624 |
+
if (lowerSection.includes('improve') || lowerSection.includes('enhance')) {
|
625 |
+
recommendations.push('Improve overall content quality');
|
626 |
+
}
|
627 |
+
if (lowerSection.includes('adjust') || lowerSection.includes('modify')) {
|
628 |
+
recommendations.push('Adjust content to meet requirements');
|
629 |
+
}
|
630 |
+
if (lowerSection.includes('review') && lowerSection.includes('carefully')) {
|
631 |
+
recommendations.push('Review content carefully');
|
632 |
+
}
|
633 |
+
if (lowerSection.includes('consider') && lowerSection.includes('changes')) {
|
634 |
+
recommendations.push('Consider suggested changes');
|
635 |
+
}
|
636 |
|
637 |
+
// FALLBACK PATTERN MATCHING
|
638 |
+
// If no grade found, try alternative patterns
|
639 |
+
if (grade === 'N/A') {
|
640 |
+
const fallbackGradePatterns = [
|
641 |
+
/Grade:\s*(\d+(?:\.\d+)?)\/100/i,
|
642 |
+
/Score:\s*(\d+(?:\.\d+)?)\/100/i,
|
643 |
+
/Rating:\s*(\d+(?:\.\d+)?)\/100/i,
|
644 |
+
/Mark:\s*(\d+(?:\.\d+)?)\/100/i,
|
645 |
+
/Points:\s*(\d+(?:\.\d+)?)\/100/i,
|
646 |
+
/(\d+(?:\.\d+)?)\/100/i,
|
647 |
+
];
|
648 |
+
|
649 |
+
for (const pattern of fallbackGradePatterns) {
|
650 |
+
const match = sectionBlock.match(pattern);
|
651 |
+
if (match) {
|
652 |
+
grade = `${match[1]}/100`;
|
653 |
+
console.log('Found fallback grade:', grade);
|
654 |
+
break;
|
655 |
+
}
|
656 |
+
}
|
657 |
+
}
|
658 |
|
659 |
+
// INFER PASS STATUS FROM GRADE IF NOT DETERMINED
|
660 |
+
if (grade !== 'N/A' && !overallPassMatch) {
|
661 |
+
const gradeNum = parseFloat(grade.split('/')[0]);
|
662 |
+
pass = gradeNum >= 80;
|
663 |
+
console.log('Inferred overall pass status from grade:', pass);
|
664 |
+
}
|
665 |
+
|
666 |
+
// FINAL PRIMARY ISSUE DETERMINATION
|
667 |
+
// If still no primary issue, generate one based on pass status
|
668 |
+
if (primaryIssue === 'Overall assessment not available.') {
|
669 |
+
if (pass) {
|
670 |
+
primaryIssue = 'All sections meet requirements';
|
671 |
+
} else {
|
672 |
+
primaryIssue = 'Some sections have violations';
|
673 |
+
}
|
674 |
+
}
|
675 |
+
|
676 |
+
console.log('Final overall result - Grade:', grade, 'Pass:', pass, 'Issue:', primaryIssue);
|
677 |
+
|
678 |
+
return {
|
679 |
+
grade,
|
680 |
+
pass,
|
681 |
primaryIssue,
|
682 |
+
detailedAssessment: detailedAssessment || undefined,
|
683 |
+
keyStrengths: keyStrengths.length > 0 ? keyStrengths : undefined,
|
684 |
+
recommendations: recommendations.length > 0 ? recommendations : undefined,
|
685 |
+
explanations: explanations || undefined,
|
686 |
rawContent: sectionBlock
|
687 |
};
|
688 |
};
|
|
|
728 |
return { detailedQaReport: defaultReport, overallPass: false, overallGrade: 'N/A' };
|
729 |
}
|
730 |
|
731 |
+
console.log('Enhanced QA parsing - input text preview:', cleanedQaText.substring(0, 500));
|
732 |
+
|
733 |
+
// COMPLETELY REWRITTEN TO HANDLE ACTUAL QA GUARD FORMAT
|
734 |
+
// The QA Guard uses formats like: "## **TITLE GRADE: 100/100 ✅ PASS**"
|
735 |
+
|
736 |
+
const parsedData: Partial<DetailedQaReport> = {};
|
737 |
+
const additionalSections: { [sectionName: string]: { content: string; type: 'assessment' | 'strengths' | 'recommendations' | 'explanations' | 'other'; } } = {};
|
738 |
+
|
739 |
+
// Enhanced section splitting to handle the actual QA Guard format
|
740 |
+
let sections: string[] = [];
|
741 |
+
|
742 |
+
// First, try to remove any leading overall evaluation header that might interfere with section splitting
|
743 |
+
const contentAfterOverallHeader = cleanedQaText.replace(/^(#\s*FINAL\s+QUALITY\s+ASSURANCE\s+EVALUATION|##\s*Section\s+Grades)\s*/i, '').trim();
|
744 |
+
|
745 |
+
// Try multiple splitting strategies for robustness
|
746 |
+
// Order matters: more specific patterns should come first
|
747 |
+
const sectionHeaderPatterns = [
|
748 |
+
// Highly specific: ### **SECTION NAME** - GRADE: X/100 ✅ PASS
|
749 |
+
/(?=(?:##|###)\s*\*\*([^*]+)\*\*\s*-\s*GRADE:)/g,
|
750 |
+
// Highly specific: ### **SECTION NAME: PASS** ✅
|
751 |
+
/(?=(?:##|###)\s*\*\*([^*]+):\s*(?:PASS|FAIL)\*\*\s*(?:✅|❌))/g,
|
752 |
+
// Specific: ## **SECTION NAME GRADE:**
|
753 |
+
/(?=(?:##|###)\s*\*\*([^*]+)\s+GRADE:)/g,
|
754 |
+
// Specific: ## **SECTION NAME** (with optional pass/fail indicator)
|
755 |
+
/(?=(?:##|###)\s*\*\*([^*]+)\*\*(?:\s*(?:✅|❌|PASS|FAIL))?)/g,
|
756 |
+
// Generic: ## Section Name
|
757 |
+
/(?=(?:##|###)\s*[^#\n]*)/g,
|
758 |
+
];
|
759 |
+
|
760 |
+
for (const pattern of sectionHeaderPatterns) {
|
761 |
+
sections = contentAfterOverallHeader.split(pattern).filter(Boolean);
|
762 |
+
if (sections.length > 1) { // If we found more than one section, this split was successful
|
763 |
+
console.log(`Using successful split pattern: ${pattern}`);
|
764 |
+
break;
|
765 |
+
}
|
766 |
+
}
|
767 |
+
|
768 |
+
// Fallback to paragraph splitting if no suitable header pattern was found
|
769 |
+
if (sections.length <= 1) {
|
770 |
+
sections = contentAfterOverallHeader.split(/\n\n+/).filter(section => section.trim().length > 20);
|
771 |
+
console.log('Using paragraph-based parsing as fallback (no header pattern matched)');
|
772 |
+
}
|
773 |
+
|
774 |
+
// Ensure sections are not empty after splitting
|
775 |
+
sections = sections.filter(s => s.trim().length > 5); // Minimum length to be considered a valid section
|
776 |
+
|
777 |
+
console.log(`Found ${sections.length} sections to parse`);
|
778 |
+
sections.forEach((section, index) => {
|
779 |
+
console.log(`Section ${index} preview:`, section.substring(0, 150));
|
780 |
+
});
|
781 |
+
|
782 |
+
// Parse each section with enhanced logic
|
783 |
+
sections.forEach((sectionBlock, index) => {
|
784 |
+
const lines = sectionBlock.trim().split('\n');
|
785 |
+
const headerRaw = lines[0]?.trim() || '';
|
786 |
+
const header = headerRaw.toLowerCase();
|
787 |
+
|
788 |
+
console.log(`Processing section ${index} with header:`, headerRaw);
|
789 |
+
|
790 |
+
let sectionType = ''; // Must be reset for each block
|
791 |
+
let sectionData: QaSectionResult | null = null;
|
792 |
+
|
793 |
+
// New, more robust section identification logic
|
794 |
+
const headerForTypeCheck = header
|
795 |
+
.replace(/^(#+\s*|\*\*)/, '')
|
796 |
+
.replace(/[:*]/g, '')
|
797 |
+
.replace(/\s*-\s*grade:.*/, '') // also strip grade info
|
798 |
+
.replace(/\s*(✅|❌|pass|fail).*/, '') // and status info
|
799 |
+
.trim();
|
800 |
+
|
801 |
+
const words = headerForTypeCheck.split(/\s+/).filter(Boolean);
|
802 |
+
|
803 |
+
// If the normalized header is just ONE clean word, it's a primary section
|
804 |
+
if (words.length === 1) {
|
805 |
+
const typeWord = words[0];
|
806 |
+
if (typeWord === 'title') sectionType = 'title';
|
807 |
+
else if (typeWord === 'meta') sectionType = 'meta';
|
808 |
+
else if (typeWord === 'h1') sectionType = 'h1';
|
809 |
+
else if (typeWord === 'copy') sectionType = 'copy';
|
810 |
+
else if (['overall', 'final', 'assessment'].includes(typeWord)) sectionType = 'overall';
|
811 |
+
}
|
812 |
+
|
813 |
+
// If not identified, check for specific multi-word headers for 'overall'
|
814 |
+
if (!sectionType) {
|
815 |
+
if (headerForTypeCheck.startsWith('overall assessment') || headerForTypeCheck.startsWith('final assessment')) {
|
816 |
+
sectionType = 'overall';
|
817 |
+
}
|
818 |
+
}
|
819 |
|
820 |
+
// Route to the correct parser or handle as an additional section
|
821 |
+
if (sectionType === 'title') {
|
822 |
+
console.log('Identified as TITLE section');
|
823 |
+
sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
|
824 |
+
if (sectionData) parsedData.title = sectionData;
|
825 |
+
} else if (sectionType === 'meta') {
|
826 |
+
console.log('Identified as META section');
|
827 |
+
sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
|
828 |
+
if (sectionData) parsedData.meta = sectionData;
|
829 |
+
} else if (sectionType === 'h1') {
|
830 |
+
console.log('Identified as H1 section');
|
831 |
+
sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
|
832 |
+
if (sectionData) parsedData.h1 = sectionData;
|
833 |
+
} else if (sectionType === 'copy') {
|
834 |
+
console.log('Identified as COPY section');
|
835 |
+
sectionData = parseActualQAGuardSection(sectionBlock, sectionType);
|
836 |
+
if (sectionData) parsedData.copy = sectionData;
|
837 |
+
} else if (sectionType === 'overall') {
|
838 |
+
console.log('Identified as OVERALL section');
|
839 |
+
const enhancedOverall = parseEnhancedOverallSection(sectionBlock);
|
840 |
+
parsedData.overall = enhancedOverall;
|
841 |
+
} else if (header.includes('grades by section') || header.includes('section grades')) {
|
842 |
+
console.log('Identified as introductory section, skipping.');
|
843 |
+
return;
|
844 |
} else {
|
845 |
+
// Additional sections logic remains here
|
846 |
+
console.log('Identified as additional section');
|
847 |
+
let displayName = headerRaw.replace(/^[#*\s-]+/g, '').trim();
|
848 |
+
// ... (existing additional section mapping logic) ...
|
849 |
+
additionalSections[displayName] = {
|
850 |
+
content: sectionBlock,
|
851 |
+
type: determineSectionType(sectionBlock)
|
852 |
+
};
|
853 |
}
|
854 |
+
});
|
855 |
+
|
856 |
+
// If no sections were found, try to extract from the complete text
|
857 |
+
if (Object.keys(parsedData).length === 0) {
|
858 |
+
console.log('No sections found, attempting full-text extraction');
|
859 |
|
860 |
+
// Try to extract grades and pass status from the raw text
|
861 |
+
const titleMatch = cleanedQaText.match(/TITLE[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
|
862 |
+
const metaMatch = cleanedQaText.match(/META[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
|
863 |
+
const h1Match = cleanedQaText.match(/H1[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
|
864 |
+
const copyMatch = cleanedQaText.match(/COPY[^:]*:\s*(\d+)\/100[^📋]*?(✅|❌|PASS|FAIL)/i);
|
865 |
|
866 |
+
if (titleMatch) {
|
867 |
+
parsedData.title = {
|
868 |
+
grade: `${titleMatch[1]}/100`,
|
869 |
+
pass: titleMatch[2] === '✅' || titleMatch[2].toUpperCase() === 'PASS',
|
870 |
+
errors: titleMatch[2] === '✅' || titleMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
|
871 |
+
corrected: 'Content analysis extracted from full report',
|
872 |
+
rawContent: cleanedQaText
|
873 |
+
};
|
874 |
+
console.log('Extracted TITLE data:', parsedData.title);
|
875 |
}
|
876 |
|
877 |
+
if (metaMatch) {
|
878 |
+
parsedData.meta = {
|
879 |
+
grade: `${metaMatch[1]}/100`,
|
880 |
+
pass: metaMatch[2] === '✅' || metaMatch[2].toUpperCase() === 'PASS',
|
881 |
+
errors: metaMatch[2] === '✅' || metaMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
|
882 |
+
corrected: 'Content analysis extracted from full report',
|
883 |
+
rawContent: cleanedQaText
|
884 |
+
};
|
885 |
+
console.log('Extracted META data:', parsedData.meta);
|
886 |
+
}
|
887 |
+
|
888 |
+
if (h1Match) {
|
889 |
+
parsedData.h1 = {
|
890 |
+
grade: `${h1Match[1]}/100`,
|
891 |
+
pass: h1Match[2] === '✅' || h1Match[2].toUpperCase() === 'PASS',
|
892 |
+
errors: h1Match[2] === '✅' || h1Match[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
|
893 |
+
corrected: 'Content analysis extracted from full report',
|
894 |
+
rawContent: cleanedQaText
|
895 |
+
};
|
896 |
+
console.log('Extracted H1 data:', parsedData.h1);
|
897 |
+
}
|
898 |
+
|
899 |
+
if (copyMatch) {
|
900 |
+
parsedData.copy = {
|
901 |
+
grade: `${copyMatch[1]}/100`,
|
902 |
+
pass: copyMatch[2] === '✅' || copyMatch[2].toUpperCase() === 'PASS',
|
903 |
+
errors: copyMatch[2] === '✅' || copyMatch[2].toUpperCase() === 'PASS' ? ['No errors reported.'] : ['Violations detected'],
|
904 |
+
corrected: 'Content analysis extracted from full report',
|
905 |
+
rawContent: cleanedQaText
|
906 |
+
};
|
907 |
+
console.log('Extracted COPY data:', parsedData.copy);
|
908 |
+
}
|
909 |
+
|
910 |
+
// Extract overall assessment
|
911 |
+
const overallMatch = cleanedQaText.match(/(?:OVERALL|Final).*?(\d+)\/100/i);
|
912 |
+
if (overallMatch) {
|
913 |
+
const overallGrade = parseInt(overallMatch[1]);
|
914 |
+
parsedData.overall = {
|
915 |
+
grade: `${overallGrade}/100`,
|
916 |
+
pass: overallGrade >= 80,
|
917 |
+
primaryIssue: overallGrade >= 80 ? 'All sections meet requirements' : 'Some violations detected',
|
918 |
+
rawContent: cleanedQaText
|
919 |
+
};
|
920 |
+
console.log('Extracted OVERALL data:', parsedData.overall);
|
921 |
+
}
|
922 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
923 |
|
924 |
+
const finalReport: DetailedQaReport = {
|
925 |
+
title: parsedData.title || { ...defaultSection, errors: ['Title section not found in QA response'] },
|
926 |
+
meta: parsedData.meta || { ...defaultSection, errors: ['Meta section not found in QA response'] },
|
927 |
+
h1: parsedData.h1 || { ...defaultSection, errors: ['H1 section not found in QA response'] },
|
928 |
+
copy: parsedData.copy || { ...defaultSection, errors: ['Copy section not found in QA response'] },
|
929 |
+
overall: parsedData.overall || { grade: 'N/A', pass: false, primaryIssue: 'Overall assessment not found in QA response' },
|
930 |
+
additionalSections: Object.keys(additionalSections).length > 0 ? additionalSections : undefined,
|
931 |
+
completeRawReport: qaText
|
932 |
+
};
|
933 |
+
|
934 |
+
// Calculate overall pass/grade from individual sections if overall not found
|
935 |
+
if (!parsedData.overall && (parsedData.title || parsedData.meta || parsedData.h1 || parsedData.copy)) {
|
936 |
+
const validSections = [parsedData.title, parsedData.meta, parsedData.h1, parsedData.copy].filter(Boolean);
|
937 |
+
const allPass = validSections.every(section => section?.pass);
|
938 |
+
const grades = validSections.map(section => {
|
939 |
+
if (section?.grade && section.grade !== 'N/A') {
|
940 |
+
const match = section.grade.match(/(\d+)/);
|
941 |
+
return match ? parseInt(match[1]) : 0;
|
942 |
}
|
943 |
+
return 0;
|
944 |
+
}).filter(g => g > 0);
|
945 |
+
|
946 |
+
const avgGrade = grades.length > 0 ? Math.round(grades.reduce((a, b) => a + b, 0) / grades.length) : 0;
|
947 |
+
|
948 |
+
finalReport.overall = {
|
949 |
+
grade: avgGrade > 0 ? `${avgGrade}/100` : 'N/A',
|
950 |
+
pass: allPass && avgGrade >= 80,
|
951 |
+
primaryIssue: allPass ? 'All sections passed' : 'Some sections have violations',
|
952 |
+
rawContent: cleanedQaText
|
953 |
+
};
|
954 |
+
|
955 |
+
console.log('Calculated overall from sections:', finalReport.overall);
|
956 |
+
}
|
957 |
+
|
958 |
+
console.log('Final QA parsing result:', {
|
959 |
+
title: finalReport.title?.grade,
|
960 |
+
meta: finalReport.meta?.grade,
|
961 |
+
h1: finalReport.h1?.grade,
|
962 |
+
copy: finalReport.copy?.grade,
|
963 |
+
overall: finalReport.overall?.grade,
|
964 |
+
overallPass: finalReport.overall?.pass
|
965 |
+
});
|
966 |
+
|
967 |
+
return {
|
968 |
+
detailedQaReport: finalReport,
|
969 |
+
overallPass: finalReport.overall.pass,
|
970 |
+
overallGrade: finalReport.overall.grade
|
971 |
+
};
|
972 |
+
};
|
973 |
|
974 |
+
/**
|
975 |
+
* Parses a single section of the QA report (e.g., TITLE, H1) based on the actual QA Guard format.
|
976 |
+
* This is a more robust parser that handles variations in header format and content.
|
977 |
+
*/
|
978 |
+
const parseActualQAGuardSection = (sectionBlock: string, sectionType: string): QaSectionResult | null => {
|
979 |
+
console.log(`Parsing ${sectionType} section with actual QA Guard format`);
|
980 |
+
console.log('Section block preview:', sectionBlock.substring(0, 200));
|
981 |
+
|
982 |
+
// Extract grade and pass status from the actual QA Guard format
|
983 |
let grade = 'N/A';
|
984 |
+
let pass = false;
|
985 |
+
let errors: string[] = ['No errors reported.'];
|
986 |
+
let corrected = 'Content analysis not available.';
|
987 |
+
let detailedAssessment = '';
|
988 |
+
let keyStrengths: string[] = [];
|
989 |
+
let recommendations: string[] = [];
|
990 |
+
let explanations = '';
|
991 |
+
|
992 |
+
// COMPREHENSIVE HEADER PATTERN MATCHING
|
993 |
+
// Look for the actual QA Guard format: "### **TITLE** ✅ PASS" or "### **TITLE** ❌ FAIL"
|
994 |
+
// Also handle variations like "## **TITLE: PASS** ✅" or "## **TITLE: FAIL** ❌"
|
995 |
+
const headerPatterns = [
|
996 |
+
/###\s*\*\*([^*]+)\*\*\s*(✅|❌)\s*(PASS|FAIL)/i, // ### **TITLE** ✅ PASS
|
997 |
+
/##\s*\*\*([^*]+):\s*(PASS|FAIL)\*\*\s*(✅|❌)/i, // ## **TITLE: PASS** ✅
|
998 |
+
/##\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)\s*(✅|❌)/i, // ## **TITLE** PASS ✅
|
999 |
+
/###\s*\*\*([^*]+):\s*(PASS|FAIL)\*\*\s*(✅|❌)/i, // ### **TITLE: PASS** ✅
|
1000 |
+
/##\s*\*\*([^*]+)\*\*\s*(✅|❌)\s*(PASS|FAIL)/i, // ## **TITLE** ✅ PASS
|
1001 |
+
/###\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)/i, // ### **TITLE** PASS
|
1002 |
+
/##\s*\*\*([^*]+)\*\*\s*(PASS|FAIL)/i, // ## **TITLE** PASS
|
1003 |
+
/###\s*\*\*([^*]+):\s*(PASS|FAIL)/i, // ### **TITLE: PASS**
|
1004 |
+
/##\s*\*\*([^*]+):\s*(PASS|FAIL)/i, // ## **TITLE: PASS**
|
1005 |
+
/###\s*([^*]+)\s*(✅|❌)\s*(PASS|FAIL)/i, // ### TITLE ✅ PASS
|
1006 |
+
/##\s*([^*]+)\s*(✅|❌)\s*(PASS|FAIL)/i, // ## TITLE ✅ PASS
|
1007 |
+
/###\s*([^*]+):\s*(PASS|FAIL)\s*(✅|❌)/i, // ### TITLE: PASS ✅
|
1008 |
+
/##\s*([^*]+):\s*(PASS|FAIL)\s*(✅|❌)/i, // ## TITLE: PASS ✅
|
1009 |
+
/###\s*([^*]+)\s*(PASS|FAIL)/i, // ### TITLE PASS
|
1010 |
+
/##\s*([^*]+)\s*(PASS|FAIL)/i, // ## TITLE PASS
|
1011 |
+
/###\s*([^*]+):\s*(PASS|FAIL)/i, // ### TITLE: PASS
|
1012 |
+
/##\s*([^*]+):\s*(PASS|FAIL)/i, // ## TITLE: PASS
|
1013 |
+
];
|
1014 |
+
|
1015 |
+
let headerMatch = null;
|
1016 |
+
for (const pattern of headerPatterns) {
|
1017 |
+
headerMatch = sectionBlock.match(pattern);
|
1018 |
+
if (headerMatch) {
|
1019 |
+
console.log('Found QA Guard header format:', headerMatch[0]);
|
1020 |
+
// Determine pass status from various indicators in the match
|
1021 |
+
const passIndicators = [headerMatch[2], headerMatch[3]].filter(Boolean);
|
1022 |
+
pass = passIndicators.some(indicator =>
|
1023 |
+
indicator === '✅' || indicator.toUpperCase() === 'PASS'
|
1024 |
+
);
|
1025 |
+
break;
|
1026 |
+
}
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
// COMPREHENSIVE GRADE PATTERN MATCHING
|
1030 |
+
// Look for grade in various formats: "- **Grade:** 100/100", "Grade: 100/100", etc.
|
1031 |
+
const gradePatterns = [
|
1032 |
+
/-?\s*\*\*Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Grade:** 100/100
|
1033 |
+
/-?\s*\*\*Grade\*\*:\s*(\d+(?:\.\d+)?)\/100/i, // - **Grade**: 100/100
|
1034 |
+
/-?\s*Grade:\s*(\d+(?:\.\d+)?)\/100/i, // - Grade: 100/100
|
1035 |
+
/-?\s*Grade\s*:\s*(\d+(?:\.\d+)?)\/100/i, // - Grade : 100/100
|
1036 |
+
/\*\*Grade:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // **Grade:** 100/100
|
1037 |
+
/\*\*Grade\*\*:\s*(\d+(?:\.\d+)?)\/100/i, // **Grade**: 100/100
|
1038 |
+
/Grade:\s*(\d+(?:\.\d+)?)\/100/i, // Grade: 100/100
|
1039 |
+
/Grade\s*:\s*(\d+(?:\.\d+)?)\/100/i, // Grade : 100/100
|
1040 |
+
/-?\s*\*\*Score:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Score:** 100/100
|
1041 |
+
/-?\s*\*\*Rating:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Rating:** 100/100
|
1042 |
+
/-?\s*\*\*Mark:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Mark:** 100/100
|
1043 |
+
/-?\s*\*\*Points:\*\*\s*(\d+(?:\.\d+)?)\/100/i, // - **Points:** 100/100
|
1044 |
+
/-?\s*\*\*(\d+(?:\.\d+)?)\/100\s*points?\*\*/i, // - **100/100 points**
|
1045 |
+
/-?\s*(\d+(?:\.\d+)?)\/100\s*points?/i, // - 100/100 points
|
1046 |
+
/-?\s*\*\*(\d+(?:\.\d+)?)\/100\*\*/i, // - **100/100**
|
1047 |
+
/-?\s*(\d+(?:\.\d+)?)\/100/i, // - 100/100
|
1048 |
+
/\*\*(\d+(?:\.\d+)?)\/100\*\*/i, // **100/100**
|
1049 |
+
/(\d+(?:\.\d+)?)\/100/i, // 100/100
|
1050 |
+
];
|
1051 |
+
|
1052 |
+
let gradeMatch = null;
|
1053 |
+
for (const pattern of gradePatterns) {
|
1054 |
+
gradeMatch = sectionBlock.match(pattern);
|
1055 |
+
if (gradeMatch) {
|
1056 |
+
grade = `${gradeMatch[1]}/100`;
|
1057 |
+
console.log('Found grade:', grade);
|
1058 |
+
break;
|
1059 |
+
}
|
1060 |
}
|
1061 |
+
|
1062 |
+
// COMPREHENSIVE COMPLIANCE PATTERN MATCHING
|
1063 |
+
// Look for compliance status in various formats
|
1064 |
+
const compliancePatterns = [
|
1065 |
+
/-?\s*\*\*Compliance:\*\*\s*([^\n]+)/i, // - **Compliance:** Full compliance
|
1066 |
+
/-?\s*\*\*Status:\*\*\s*([^\n]+)/i, // - **Status:** Passed
|
1067 |
+
/-?\s*\*\*Result:\*\*\s*([^\n]+)/i, // - **Result:** Successful
|
1068 |
+
/-?\s*\*\*Assessment:\*\*\s*([^\n]+)/i, // - **Assessment:** Compliant
|
1069 |
+
/-?\s*\*\*Evaluation:\*\*\s*([^\n]+)/i, // - **Evaluation:** Pass
|
1070 |
+
/-?\s*\*\*Check:\*\*\s*([^\n]+)/i, // - **Check:** OK
|
1071 |
+
/-?\s*\*\*Verification:\*\*\s*([^\n]+)/i, // - **Verification:** Valid
|
1072 |
+
/-?\s*\*\*Review:\*\*\s*([^\n]+)/i, // - **Review:** Approved
|
1073 |
+
/-?\s*\*\*Analysis:\*\*\s*([^\n]+)/i, // - **Analysis:** Compliant
|
1074 |
+
/-?\s*\*\*Summary:\*\*\s*([^\n]+)/i, // - **Summary:** Pass
|
1075 |
+
];
|
1076 |
+
|
1077 |
+
let complianceMatch = null;
|
1078 |
+
for (const pattern of compliancePatterns) {
|
1079 |
+
complianceMatch = sectionBlock.match(pattern);
|
1080 |
+
if (complianceMatch) {
|
1081 |
+
const compliance = complianceMatch[1].trim();
|
1082 |
+
console.log('Found compliance:', compliance);
|
1083 |
+
|
1084 |
+
// Determine pass status from compliance text
|
1085 |
+
const failIndicators = ['violation', 'fail', 'error', 'non-compliant', 'rejected', 'invalid', 'incorrect', 'missing', 'below', 'above', 'out of range'];
|
1086 |
+
const passIndicators = ['compliance', 'pass', 'success', 'valid', 'correct', 'approved', 'compliant', 'within range', 'meets', 'satisfies'];
|
1087 |
+
|
1088 |
+
const lowerCompliance = compliance.toLowerCase();
|
1089 |
+
if (failIndicators.some(indicator => lowerCompliance.includes(indicator))) {
|
1090 |
+
pass = false;
|
1091 |
+
} else if (passIndicators.some(indicator => lowerCompliance.includes(indicator))) {
|
1092 |
+
pass = true;
|
1093 |
+
}
|
1094 |
+
break;
|
1095 |
+
}
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
// COMPREHENSIVE ANALYSIS PATTERN MATCHING
|
1099 |
+
// Look for analysis content in various formats
|
1100 |
+
const analysisPatterns = [
|
1101 |
+
/-?\s*\*\*Analysis:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Analysis:** Detailed text
|
1102 |
+
/-?\s*\*\*Review:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Review:** Detailed text
|
1103 |
+
/-?\s*\*\*Assessment:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Assessment:** Detailed text
|
1104 |
+
/-?\s*\*\*Evaluation:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Evaluation:** Detailed text
|
1105 |
+
/-?\s*\*\*Summary:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Summary:** Detailed text
|
1106 |
+
/-?\s*\*\*Details:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Details:** Detailed text
|
1107 |
+
/-?\s*\*\*Breakdown:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Breakdown:** Detailed text
|
1108 |
+
/-?\s*\*\*Explanation:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Explanation:** Detailed text
|
1109 |
+
/-?\s*\*\*Comments:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Comments:** Detailed text
|
1110 |
+
/-?\s*\*\*Notes:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Notes:** Detailed text
|
1111 |
+
];
|
1112 |
+
|
1113 |
+
let analysisMatch = null;
|
1114 |
+
for (const pattern of analysisPatterns) {
|
1115 |
+
analysisMatch = sectionBlock.match(pattern);
|
1116 |
+
if (analysisMatch) {
|
1117 |
+
detailedAssessment = analysisMatch[1].trim();
|
1118 |
+
console.log('Found analysis:', detailedAssessment.substring(0, 100));
|
1119 |
+
break;
|
1120 |
+
}
|
1121 |
}
|
1122 |
+
|
1123 |
+
// COMPREHENSIVE ERROR PATTERN MATCHING
|
1124 |
+
// Look for error information in various formats
|
1125 |
+
const errorPatterns = [
|
1126 |
+
/-?\s*\*\*Error:\*\*\s*([^\n]+)/i, // - **Error:** Error message
|
1127 |
+
/-?\s*\*\*Errors:\*\*\s*([^\n]+)/i, // - **Errors:** Error messages
|
1128 |
+
/-?\s*\*\*Issue:\*\*\s*([^\n]+)/i, // - **Issue:** Issue description
|
1129 |
+
/-?\s*\*\*Issues:\*\*\s*([^\n]+)/i, // - **Issues:** Issue descriptions
|
1130 |
+
/-?\s*\*\*Problem:\*\*\s*([^\n]+)/i, // - **Problem:** Problem description
|
1131 |
+
/-?\s*\*\*Problems:\*\*\s*([^\n]+)/i, // - **Problems:** Problem descriptions
|
1132 |
+
/-?\s*\*\*Violation:\*\*\s*([^\n]+)/i, // - **Violation:** Violation details
|
1133 |
+
/-?\s*\*\*Violations:\*\*\s*([^\n]+)/i, // - **Violations:** Violation details
|
1134 |
+
/-?\s*\*\*Warning:\*\*\s*([^\n]+)/i, // - **Warning:** Warning message
|
1135 |
+
/-?\s*\*\*Warnings:\*\*\s*([^\n]+)/i, // - **Warnings:** Warning messages
|
1136 |
+
/-?\s*\*\*Concern:\*\*\s*([^\n]+)/i, // - **Concern:** Concern details
|
1137 |
+
/-?\s*\*\*Concerns:\*\*\s*([^\n]+)/i, // - **Concerns:** Concern details
|
1138 |
+
];
|
1139 |
+
|
1140 |
+
let errorMatch = null;
|
1141 |
+
for (const pattern of errorPatterns) {
|
1142 |
+
errorMatch = sectionBlock.match(pattern);
|
1143 |
+
if (errorMatch) {
|
1144 |
+
errors = [errorMatch[1].trim()];
|
1145 |
+
console.log('Found error:', errors[0]);
|
1146 |
+
break;
|
1147 |
+
}
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
// ENHANCED: Look for multi-line violation lists
|
1151 |
+
const violationsMatch = sectionBlock.match(/-?\s*\*\*(?:Structure|Major|Minor)\s+violations?\*\*:\s*([\s\S]*?)(?=\n-?\s*\*\*|\n\n|---|$)/i);
|
1152 |
+
if (violationsMatch) {
|
1153 |
+
const violationText = violationsMatch[1].trim();
|
1154 |
+
// Split by lines that start with the violation marker
|
1155 |
+
const violationErrors = violationText.split(/\n\s*(?:-|\d+\.)\s*(?:❌|\⚠️)?\s*\*\*/g)
|
1156 |
+
.map(line => {
|
1157 |
+
// Clean up the line to get the core violation text
|
1158 |
+
return line.replace(/MAJOR VIOLATION \([^)]+\):/, '')
|
1159 |
+
.replace(/MINOR VIOLATION \([^)]+\):/, '')
|
1160 |
+
.replace(/\*\*$/, '')
|
1161 |
+
.trim();
|
1162 |
+
})
|
1163 |
+
.filter(line => line.length > 10); // Filter out empty or trivial lines
|
1164 |
+
|
1165 |
+
if (violationErrors.length > 0) {
|
1166 |
+
if (errors[0] === 'No errors reported.') {
|
1167 |
+
errors = violationErrors;
|
1168 |
} else {
|
1169 |
+
// Prepend violation details to any existing errors
|
1170 |
+
errors = [...violationErrors, ...errors];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1171 |
}
|
1172 |
+
console.log('Found detailed violation errors:', violationErrors);
|
1173 |
+
}
|
1174 |
+
}
|
1175 |
+
|
1176 |
+
// COMPREHENSIVE CORRECTED CONTENT PATTERN MATCHING
|
1177 |
+
// Look for corrected content in various formats
|
1178 |
+
const correctedPatterns = [
|
1179 |
+
/\*\*CORRECTED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **CORRECTED META:**\n```\ncontent\n```
|
1180 |
+
/\*\*CORRECTED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **CORRECTED META:**\ncontent
|
1181 |
+
/\*\*FIXED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **FIXED META:**\n```\ncontent\n```
|
1182 |
+
/\*\*FIXED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **FIXED META:**\ncontent
|
1183 |
+
/\*\*REVISED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **REVISED META:**\n```\ncontent\n```
|
1184 |
+
/\*\*REVISED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **REVISED META:**\ncontent
|
1185 |
+
/\*\*UPDATED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **UPDATED META:**\n```\ncontent\n```
|
1186 |
+
/\*\*UPDATED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **UPDATED META:**\ncontent
|
1187 |
+
/\*\*SUGGESTED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **SUGGESTED META:**\n```\ncontent\n```
|
1188 |
+
/\*\*SUGGESTED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **SUGGESTED META:**\ncontent
|
1189 |
+
/\*\*RECOMMENDED\s+[A-Z]+\*\*:\s*\n```\n([\s\S]*?)\n```/i, // **RECOMMENDED META:**\n```\ncontent\n```
|
1190 |
+
/\*\*RECOMMENDED\s+[A-Z]+\*\*:\s*\n([\s\S]*?)(?=\n\*\*|$)/i, // **RECOMMENDED META:**\ncontent
|
1191 |
+
/-?\s*\*\*Corrected:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Corrected:** content
|
1192 |
+
/-?\s*\*\*Fixed:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Fixed:** content
|
1193 |
+
/-?\s*\*\*Revised:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Revised:** content
|
1194 |
+
/-?\s*\*\*Updated:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Updated:** content
|
1195 |
+
/-?\s*\*\*Suggested:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Suggested:** content
|
1196 |
+
/-?\s*\*\*Recommended:\*\*\s*([^\n]+(?:\n(?!-?\s*\*\*)[^\n]+)*)/i, // - **Recommended:** content
|
1197 |
+
];
|
1198 |
+
|
1199 |
+
let correctedMatch = null;
|
1200 |
+
for (const pattern of correctedPatterns) {
|
1201 |
+
correctedMatch = sectionBlock.match(pattern);
|
1202 |
+
if (correctedMatch) {
|
1203 |
+
corrected = correctedMatch[1].trim();
|
1204 |
+
console.log('Found corrected content:', corrected.substring(0, 100));
|
1205 |
+
break;
|
1206 |
}
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
// ENHANCED KEY STRENGTHS AND RECOMMENDATIONS EXTRACTION
|
1210 |
+
// Extract key strengths and recommendations from the analysis and compliance text
|
1211 |
+
if (detailedAssessment) {
|
1212 |
+
// Look for positive indicators in analysis
|
1213 |
+
const positiveIndicators = [
|
1214 |
+
'compliance', 'compliant', 'proper', '✓', 'check', 'valid', 'correct', 'appropriate',
|
1215 |
+
'meets', 'satisfies', 'within range', 'successful', 'approved', 'pass', 'good', 'excellent',
|
1216 |
+
'optimal', 'ideal', 'perfect', 'complete', 'thorough', 'comprehensive', 'effective'
|
1217 |
+
];
|
1218 |
|
1219 |
+
const negativeIndicators = [
|
1220 |
+
'violation', 'error', 'fail', 'problem', 'issue', 'concern', 'warning', 'missing',
|
1221 |
+
'below', 'above', 'out of range', 'incorrect', 'invalid', 'non-compliant', 'rejected',
|
1222 |
+
'insufficient', 'inadequate', 'poor', 'weak', 'deficient'
|
1223 |
+
];
|
1224 |
|
1225 |
+
const lowerAnalysis = detailedAssessment.toLowerCase();
|
1226 |
+
|
1227 |
+
// Extract key strengths
|
1228 |
+
if (positiveIndicators.some(indicator => lowerAnalysis.includes(indicator))) {
|
1229 |
+
keyStrengths.push('Meets compliance requirements');
|
1230 |
+
}
|
1231 |
+
if (lowerAnalysis.includes('keyword') && (lowerAnalysis.includes('included') || lowerAnalysis.includes('present'))) {
|
1232 |
+
keyStrengths.push('Proper keyword integration');
|
1233 |
+
}
|
1234 |
+
if (lowerAnalysis.includes('tone') && lowerAnalysis.includes('appropriate')) {
|
1235 |
+
keyStrengths.push('Appropriate tone maintained');
|
1236 |
+
}
|
1237 |
+
if (lowerAnalysis.includes('character') && lowerAnalysis.includes('within')) {
|
1238 |
+
keyStrengths.push('Character count within requirements');
|
1239 |
+
}
|
1240 |
+
if (lowerAnalysis.includes('length') && lowerAnalysis.includes('✓')) {
|
1241 |
+
keyStrengths.push('Length requirements met');
|
1242 |
+
}
|
1243 |
+
if (lowerAnalysis.includes('structure') && lowerAnalysis.includes('proper')) {
|
1244 |
+
keyStrengths.push('Proper structure maintained');
|
1245 |
+
}
|
1246 |
+
|
1247 |
+
// Extract recommendations
|
1248 |
+
if (negativeIndicators.some(indicator => lowerAnalysis.includes(indicator))) {
|
1249 |
+
recommendations.push('Address identified violations');
|
1250 |
+
}
|
1251 |
+
if (lowerAnalysis.includes('character') && (lowerAnalysis.includes('count') || lowerAnalysis.includes('length'))) {
|
1252 |
+
recommendations.push('Adjust character count to meet requirements');
|
1253 |
+
}
|
1254 |
+
if (lowerAnalysis.includes('word') && lowerAnalysis.includes('count')) {
|
1255 |
+
recommendations.push('Adjust word count to meet requirements');
|
1256 |
+
}
|
1257 |
+
if (lowerAnalysis.includes('keyword') && lowerAnalysis.includes('missing')) {
|
1258 |
+
recommendations.push('Include required keywords');
|
1259 |
+
}
|
1260 |
+
if (lowerAnalysis.includes('tone') && lowerAnalysis.includes('inappropriate')) {
|
1261 |
+
recommendations.push('Adjust tone to meet requirements');
|
1262 |
+
}
|
1263 |
+
if (lowerAnalysis.includes('structure') && lowerAnalysis.includes('improve')) {
|
1264 |
+
recommendations.push('Improve content structure');
|
1265 |
+
}
|
1266 |
}
|
1267 |
+
|
1268 |
+
// FALLBACK PATTERN MATCHING
|
1269 |
+
// If no grade found, try alternative patterns
|
1270 |
+
if (grade === 'N/A') {
|
1271 |
+
const fallbackGradePatterns = [
|
1272 |
+
/Grade:\s*(\d+(?:\.\d+)?)\/100/i,
|
1273 |
+
/Score:\s*(\d+(?:\.\d+)?)\/100/i,
|
1274 |
+
/Rating:\s*(\d+(?:\.\d+)?)\/100/i,
|
1275 |
+
/Mark:\s*(\d+(?:\.\d+)?)\/100/i,
|
1276 |
+
/Points:\s*(\d+(?:\.\d+)?)\/100/i,
|
1277 |
+
/(\d+(?:\.\d+)?)\/100/i,
|
1278 |
+
];
|
1279 |
+
|
1280 |
+
for (const pattern of fallbackGradePatterns) {
|
1281 |
+
const match = sectionBlock.match(pattern);
|
1282 |
+
if (match) {
|
1283 |
+
grade = `${match[1]}/100`;
|
1284 |
+
console.log('Found fallback grade:', grade);
|
1285 |
+
break;
|
1286 |
+
}
|
1287 |
+
}
|
1288 |
+
}
|
1289 |
+
|
1290 |
+
// INFER PASS STATUS FROM GRADE IF NOT DETERMINED
|
1291 |
+
if (grade !== 'N/A' && !headerMatch && !complianceMatch) {
|
1292 |
+
const gradeNum = parseFloat(grade.split('/')[0]);
|
1293 |
+
pass = gradeNum >= 80;
|
1294 |
+
console.log('Inferred pass status from grade:', pass);
|
1295 |
+
}
|
1296 |
+
|
1297 |
+
// FINAL PASS STATUS DETERMINATION
|
1298 |
+
// If still no pass status, check for pass/fail indicators in text
|
1299 |
+
if (!headerMatch && !complianceMatch && grade === 'N/A') {
|
1300 |
+
const lowerSection = sectionBlock.toLowerCase();
|
1301 |
+
if (lowerSection.includes('pass') && !lowerSection.includes('fail')) {
|
1302 |
+
pass = true;
|
1303 |
+
} else if (lowerSection.includes('fail')) {
|
1304 |
+
pass = false;
|
1305 |
+
} else if (lowerSection.includes('✅') && !lowerSection.includes('❌')) {
|
1306 |
+
pass = true;
|
1307 |
+
} else if (lowerSection.includes('❌')) {
|
1308 |
+
pass = false;
|
1309 |
+
}
|
1310 |
+
}
|
1311 |
+
|
1312 |
+
// UPDATE ERRORS BASED ON PASS STATUS
|
1313 |
+
if (pass && errors[0] === 'No errors reported.') {
|
1314 |
+
// Keep as is
|
1315 |
+
} else if (!pass && errors[0] === 'No errors reported.') {
|
1316 |
+
errors = ['Violations detected'];
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
console.log(`Final ${sectionType} result - Grade: ${grade}, Pass: ${pass}, Errors: ${errors.length}`);
|
1320 |
+
|
1321 |
+
return {
|
1322 |
+
grade,
|
1323 |
+
pass,
|
1324 |
+
errors,
|
1325 |
+
corrected,
|
1326 |
+
detailedAssessment: detailedAssessment || undefined,
|
1327 |
+
keyStrengths: keyStrengths.length > 0 ? keyStrengths : undefined,
|
1328 |
+
recommendations: recommendations.length > 0 ? recommendations : undefined,
|
1329 |
+
explanations: explanations || undefined,
|
1330 |
+
rawContent: sectionBlock
|
1331 |
+
};
|
1332 |
};
|
1333 |
|
1334 |
|