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 |
|