|
|
| function formatPassageTables(text) { return text; } |
| function parseAptitudeContent(raw) { |
| let contentToParse = raw; |
| const revisionMatch = contentToParse.match(/(?:^|\n)\d+\.\s+QUICK REVISION CARD/i); |
| if (revisionMatch && revisionMatch.index !== undefined) { |
| contentToParse = contentToParse.slice(0, revisionMatch.index); |
| } |
|
|
| contentToParse = formatPassageTables(contentToParse); |
|
|
| const parts = contentToParse.split(/(?:^|\n)(Q\d+)\.\s+/); |
| const questions = []; |
|
|
| let currentPassage = ''; |
| if (parts.length > 0) { |
| const rawIntro = parts[0]; |
| const pMatch = rawIntro.match(/(?:^|\n)[*#\s]*(?:\d+\.)?[*#\s]*PRACTICE SET[^\n]*\n(?:[^\n]*Each question is followed by[^\n]*\n)?(?:[^\n]*Type \d+[^\n]*\n)?([\s\S]*)/i); |
| const potentialPassage = pMatch ? pMatch[1].trim() : rawIntro.trim(); |
| const introSections = potentialPassage.split(/\n{2,}/); |
| let foundPassage = ''; |
|
|
| for (let j = introSections.length - 1; j >= 0; j--) { |
| const sec = introSections[j].trim(); |
| if (/^(?:Directions|Passage|Read the|Set \d+|Study the)/i.test(sec)) { |
| foundPassage = introSections.slice(j).join('\n\n'); |
| break; |
| } |
| } |
|
|
| if (foundPassage) currentPassage = foundPassage; |
| else if (pMatch) currentPassage = potentialPassage; |
| else if (potentialPassage.length < 500) currentPassage = potentialPassage; |
| else currentPassage = ''; |
| } |
|
|
| for (let i = 1; i < parts.length; i += 2) { |
| const qLabel = parts[i]; |
| const body = parts[i + 1] ?? ''; |
|
|
| const answerIdx = body.search(/(?:^|\n)Answer:/im); |
| const questionPart = answerIdx >= 0 ? body.slice(0, answerIdx) : body; |
| const afterAnswer = answerIdx >= 0 ? body.slice(answerIdx) : ''; |
|
|
| const optRegex = /^[ \t]*([A-Ea-e][.)]\s+[^\n]*)/gm; |
| let options[] = []; |
| let firstOptPos = -1; |
| let m; |
| while ((m = optRegex.exec(questionPart)) !== null) { |
| if (firstOptPos < 0) firstOptPos = m.index; |
| options.push(m[1].trim()); |
| } |
| if (options.length === 1) { options.length = 0; firstOptPos = -1; } |
|
|
| |
| if (options.length === 0) { |
| const inlineOptRegex = /(?:^|\s+)([A-Ea-e][.)]\s+(?:(?!\s+[A-Ea-e][.)]\s+)[\s\S])*)/g; |
| const tempOptions: { text; index: number; letter }[] = []; |
| let m2; |
| while ((m2 = inlineOptRegex.exec(questionPart)) !== null) { |
| tempOptions.push({ |
| text: m2[1].trim(), |
| index: m2.index, |
| letter: m2[1].charAt(0).toUpperCase(), |
| }); |
| } |
|
|
| const startIdx = tempOptions.findIndex((o) => o.letter === 'A'); |
| if (startIdx >= 0) { |
| const validOpts = []; |
| let expectedLetterCode = 65; |
| for (let k = startIdx; k < tempOptions.length; k++) { |
| if (tempOptions[k].letter.charCodeAt(0) === expectedLetterCode) { |
| validOpts.push(tempOptions[k]); |
| expectedLetterCode++; |
| } else { |
| break; |
| } |
| } |
| if (validOpts.length >= 2) { |
| firstOptPos = validOpts[0].index; |
| options = validOpts.map((o) => o.text); |
| } |
| } |
| } |
|
|
| const questionText = firstOptPos > 0 ? questionPart.slice(0, firstOptPos).trim() : questionPart.trim(); |
|
|
| const expIdx = afterAnswer.search(/(?:^|\n)Explanation:/im); |
| let answerRaw = afterAnswer; |
| let explanation = ''; |
| let nextPassage = currentPassage; |
|
|
| if (expIdx >= 0) { |
| answerRaw = afterAnswer.slice(0, expIdx); |
| const afterExplText = afterAnswer.slice(expIdx); |
| const sections = afterExplText.split(/\n{2,}/); |
| explanation = sections[0].replace(/(?:^|\n)Explanation:\s*/im, '').trim(); |
| const possiblePassage = sections.slice(1).join('\n\n').trim(); |
| if (possiblePassage.length > 0) { |
| const isPassageMarker = /^(?:Directions|Passage|Read the|Set \d+|Use the following|BAR GRAPH|PIE CHART|LINE GRAPH|CASELET|MIXED DI|RADAR CHART|Student|Department|A company|The following|Refer to|Study the)/i.test(possiblePassage); |
| const hasPipeTable = possiblePassage.split('\n').some(l => (l.includes('|') || l.includes('│')) && l.split(/\||│/).length >= 3); |
| if (isPassageMarker || hasPipeTable) nextPassage = possiblePassage; |
| else if (possiblePassage.length > 250) nextPassage = possiblePassage; |
| else explanation += '\n\n' + possiblePassage; |
| } |
| } else { |
| answerRaw = afterAnswer; |
| } |
|
|
| const answer = answerRaw.replace(/(?:^|\n)Answer:\s*/im, '').trim(); |
|
|
| if (options.length === 0) { |
| const hasA = /\([A-Ea-e]\)/.test(questionText); |
| const isAnsLetter = /^[A-Ea-e]$/.test(answer); |
| if (hasA || isAnsLetter) { |
| let maxLetter = 'D'; |
| if (/\([Ee]\)/.test(questionText) || answer.toUpperCase() === 'E') maxLetter = 'E'; |
| const numOpts = maxLetter === 'E' ? 5 : 4; |
| for (let j = 0; j < numOpts; j++) options.push(String.fromCharCode(65 + j)); |
| } |
| } |
|
|
| if (!questionText) continue; |
| questions.push({ id: `${qLabel}-${i}`, passage: currentPassage, questionText, options, answer, explanation }); |
| currentPassage = nextPassage; |
| } |
| return questions; |
| } |
| |
| const testText = `Q1. What were the total sales in 2020? |
| Statement 1: Q3 sales in 2020 were ₹35 lakhs. |
| Statement 2: Total sales in 2021 were ₹140 lakhs. |
| A) Statement 1 alone B) Statement 2 alone C) Both together D) Either alone E) Neither |
| Answer: A) Statement 1 alone |
| Explanation: 2020 total = 25+30+Q3+20. With Q3=35 → total=110. St1 alone sufficient. Answer A.`; |
| |
| console.log(JSON.stringify(parseAptitudeContent(testText), null, 2)); |
| |