complexity-adapter.js•21.4 kB
/**
* Complexity Adaptation System
* @module content-generation/complexity-adapter
* @description Adapts assessment complexity for different grade levels
* @version 5.0.0-alpha
*/
export class ComplexityAdapter {
constructor(config = {}) {
this.config = {
defaultGradeLevel: 'medio',
preserveEducationalValue: true,
adaptVocabulary: true,
adaptQuestionLength: true,
adaptConceptDepth: true,
...config
};
// Grade level complexity frameworks
this.complexityFrameworks = {
'fundamental-1': {
name: 'Ensino Fundamental I',
vocabularyLevel: 'simple',
questionLength: 'short',
conceptDepth: 'basic',
questionTypes: ['knowledge', 'comprehension'],
maxOptions: 3,
distractorComplexity: 'obvious',
sentenceStructure: 'simple',
abstractionLevel: 'concrete',
cognitiveLoad: 'low'
},
'fundamental-2': {
name: 'Ensino Fundamental II',
vocabularyLevel: 'intermediate',
questionLength: 'medium',
conceptDepth: 'moderate',
questionTypes: ['knowledge', 'comprehension', 'application'],
maxOptions: 4,
distractorComplexity: 'plausible',
sentenceStructure: 'compound',
abstractionLevel: 'semi-concrete',
cognitiveLoad: 'medium'
},
'medio': {
name: 'Ensino Médio',
vocabularyLevel: 'advanced',
questionLength: 'detailed',
conceptDepth: 'comprehensive',
questionTypes: ['application', 'analysis', 'synthesis'],
maxOptions: 5,
distractorComplexity: 'challenging',
sentenceStructure: 'complex',
abstractionLevel: 'abstract',
cognitiveLoad: 'high'
},
'superior': {
name: 'Ensino Superior',
vocabularyLevel: 'professional',
questionLength: 'complex',
conceptDepth: 'expert',
questionTypes: ['analysis', 'synthesis', 'evaluation'],
maxOptions: 5,
distractorComplexity: 'expert',
sentenceStructure: 'sophisticated',
abstractionLevel: 'highly-abstract',
cognitiveLoad: 'very-high'
}
};
// Vocabulary adaptation dictionaries
this.vocabularyMappings = this.initializeVocabularyMappings();
}
/**
* Adapt entire assessment for grade level
* @param {Object} assessment - Assessment package
* @param {string} gradeLevel - Target grade level
* @returns {Promise<Object>} Adapted assessment
*/
async adaptAssessment(assessment, gradeLevel) {
console.log(`[COMPLEXITY-ADAPTER] Adapting assessment for grade level: ${gradeLevel}`);
if (!gradeLevel || gradeLevel === 'auto') {
gradeLevel = this.config.defaultGradeLevel;
}
const framework = this.complexityFrameworks[gradeLevel];
if (!framework) {
console.warn(`[COMPLEXITY-ADAPTER] Unknown grade level: ${gradeLevel}, using default`);
gradeLevel = this.config.defaultGradeLevel;
}
try {
const adapted = { ...assessment };
// Adapt flashcards
if (adapted.flashcards) {
adapted.flashcards = await this.adaptFlashcards(adapted.flashcards, gradeLevel);
}
// Adapt quiz questions
if (adapted.quiz && adapted.quiz.questions) {
adapted.quiz.questions = await this.adaptQuestions(adapted.quiz.questions, gradeLevel);
}
// Update metadata
if (adapted.metadata) {
adapted.metadata.adaptedGradeLevel = gradeLevel;
adapted.metadata.complexityFramework = framework.name;
adapted.metadata.adaptationTimestamp = new Date().toISOString();
}
console.log(`[COMPLEXITY-ADAPTER] Assessment adapted for ${framework.name}`);
return adapted;
} catch (error) {
console.error('[COMPLEXITY-ADAPTER] Adaptation failed:', error);
throw new Error(`Complexity adaptation failed: ${error.message}`);
}
}
/**
* Adapt flashcards for grade level
* @param {Array} flashcards - Flashcards array
* @param {string} gradeLevel - Target grade level
* @returns {Promise<Array>} Adapted flashcards
*/
async adaptFlashcards(flashcards, gradeLevel) {
const framework = this.complexityFrameworks[gradeLevel];
const adapted = [];
for (const flashcard of flashcards) {
const adaptedCard = await this.adaptFlashcard(flashcard, framework);
adapted.push(adaptedCard);
}
return adapted;
}
/**
* Adapt individual flashcard
* @param {Object} flashcard - Flashcard object
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted flashcard
*/
async adaptFlashcard(flashcard, framework) {
const adapted = { ...flashcard };
// Adapt vocabulary
if (this.config.adaptVocabulary) {
adapted.front = this.adaptVocabulary(adapted.front, framework.vocabularyLevel);
adapted.back = this.adaptVocabulary(adapted.back, framework.vocabularyLevel);
}
// Adapt sentence structure
adapted.front = this.adaptSentenceStructure(adapted.front, framework.sentenceStructure);
adapted.back = this.adaptSentenceStructure(adapted.back, framework.sentenceStructure);
// Adapt concept depth
if (this.config.adaptConceptDepth) {
adapted.back = this.adaptConceptDepth(adapted.back, framework.conceptDepth);
}
// Add adaptation metadata
adapted.adaptation = {
originalDifficulty: flashcard.difficulty,
targetFramework: framework.name,
adaptedAt: new Date().toISOString(),
adaptationMethods: ['vocabulary', 'sentence_structure', 'concept_depth']
};
return adapted;
}
/**
* Adapt quiz questions for grade level
* @param {Array} questions - Questions array
* @param {string} gradeLevel - Target grade level
* @returns {Promise<Array>} Adapted questions
*/
async adaptQuestions(questions, gradeLevel) {
const framework = this.complexityFrameworks[gradeLevel];
const adapted = [];
for (const question of questions) {
const adaptedQuestion = await this.adaptQuestion(question, framework);
if (adaptedQuestion) { // Some questions may be filtered out
adapted.push(adaptedQuestion);
}
}
return adapted;
}
/**
* Adapt individual question
* @param {Object} question - Question object
* @param {Object} framework - Complexity framework
* @returns {Promise<Object|null>} Adapted question or null if filtered
*/
async adaptQuestion(question, framework) {
// Filter out inappropriate question types for grade level
if (question.category && !framework.questionTypes.includes(question.category)) {
return null; // Filter out this question type
}
const adapted = { ...question };
// Adapt question text
if (this.config.adaptVocabulary) {
adapted.question = this.adaptVocabulary(adapted.question, framework.vocabularyLevel);
}
// Adapt question length
if (this.config.adaptQuestionLength) {
adapted.question = this.adaptQuestionLength(adapted.question, framework.questionLength);
}
// Adapt sentence structure
adapted.question = this.adaptSentenceStructure(adapted.question, framework.sentenceStructure);
// Adapt specific question types
switch (question.type) {
case 'multiple_choice':
adapted = await this.adaptMultipleChoice(adapted, framework);
break;
case 'short_answer':
adapted = await this.adaptShortAnswer(adapted, framework);
break;
case 'fill_blank':
adapted = await this.adaptFillBlank(adapted, framework);
break;
case 'true_false':
adapted = await this.adaptTrueFalse(adapted, framework);
break;
case 'matching':
adapted = await this.adaptMatching(adapted, framework);
break;
}
// Add adaptation metadata
adapted.adaptation = {
originalDifficulty: question.difficulty,
targetFramework: framework.name,
adaptedAt: new Date().toISOString(),
cognitiveLoad: framework.cognitiveLoad,
abstractionLevel: framework.abstractionLevel
};
return adapted;
}
/**
* Adapt multiple choice question
* @param {Object} question - Multiple choice question
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted question
*/
async adaptMultipleChoice(question, framework) {
const adapted = { ...question };
// Limit number of options based on grade level
if (adapted.options && adapted.options.length > framework.maxOptions) {
const correctAnswer = adapted.options[adapted.correct];
// Keep correct answer and random selection of distractors
const otherOptions = adapted.options.filter((_, index) => index !== adapted.correct);
const selectedDistractors = this.selectBestDistractors(
otherOptions,
framework.maxOptions - 1,
framework.distractorComplexity
);
adapted.options = [correctAnswer, ...selectedDistractors];
adapted.correct = 0; // Correct answer is now first, will be randomized later
}
// Adapt option vocabulary
if (this.config.adaptVocabulary && adapted.options) {
adapted.options = adapted.options.map(option =>
this.adaptVocabulary(option, framework.vocabularyLevel)
);
}
// Adapt explanation
if (adapted.explanation) {
adapted.explanation = this.adaptVocabulary(adapted.explanation, framework.vocabularyLevel);
adapted.explanation = this.adaptConceptDepth(adapted.explanation, framework.conceptDepth);
}
return adapted;
}
/**
* Adapt short answer question
* @param {Object} question - Short answer question
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted question
*/
async adaptShortAnswer(question, framework) {
const adapted = { ...question };
// Adapt expected elements
if (adapted.expectedElements) {
adapted.expectedElements = adapted.expectedElements.map(element =>
this.adaptVocabulary(element, framework.vocabularyLevel)
);
// Adjust number of expected elements based on cognitive load
const maxElements = this.getMaxElementsForCognitiveLoad(framework.cognitiveLoad);
if (adapted.expectedElements.length > maxElements) {
adapted.expectedElements = adapted.expectedElements.slice(0, maxElements);
}
}
// Adapt rubric
if (adapted.rubric) {
adapted.rubric = this.adaptVocabulary(adapted.rubric, framework.vocabularyLevel);
adapted.rubric = this.adaptConceptDepth(adapted.rubric, framework.conceptDepth);
}
return adapted;
}
/**
* Adapt fill-in-the-blank question
* @param {Object} question - Fill blank question
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted question
*/
async adaptFillBlank(question, framework) {
const adapted = { ...question };
// Adapt hints if they exist
if (adapted.hints) {
adapted.hints = adapted.hints.map(hint =>
this.adaptVocabulary(hint, framework.vocabularyLevel)
);
// Provide more hints for lower grade levels
if (framework.cognitiveLoad === 'low' && adapted.hints.length < 2) {
adapted.hints.push(this.generateAdditionalHint(adapted.answer, framework));
}
}
return adapted;
}
/**
* Adapt true/false question
* @param {Object} question - True/false question
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted question
*/
async adaptTrueFalse(question, framework) {
const adapted = { ...question };
// Adapt explanation
if (adapted.explanation) {
adapted.explanation = this.adaptVocabulary(adapted.explanation, framework.vocabularyLevel);
adapted.explanation = this.adaptSentenceStructure(adapted.explanation, framework.sentenceStructure);
}
return adapted;
}
/**
* Adapt matching question
* @param {Object} question - Matching question
* @param {Object} framework - Complexity framework
* @returns {Promise<Object>} Adapted question
*/
async adaptMatching(question, framework) {
const adapted = { ...question };
// Limit number of pairs based on cognitive load
const maxPairs = this.getMaxPairsForCognitiveLoad(framework.cognitiveLoad);
if (adapted.leftColumn && adapted.leftColumn.length > maxPairs) {
// Keep first maxPairs items
adapted.leftColumn = adapted.leftColumn.slice(0, maxPairs);
adapted.rightColumn = adapted.rightColumn.slice(0, maxPairs);
adapted.correctPairs = adapted.correctPairs.filter(pair =>
pair.left < maxPairs && pair.right < maxPairs
);
}
// Adapt vocabulary in columns
if (this.config.adaptVocabulary) {
if (adapted.leftColumn) {
adapted.leftColumn = adapted.leftColumn.map(item =>
this.adaptVocabulary(item, framework.vocabularyLevel)
);
}
if (adapted.rightColumn) {
adapted.rightColumn = adapted.rightColumn.map(item =>
this.adaptVocabulary(item, framework.vocabularyLevel)
);
}
}
return adapted;
}
/**
* Adapt vocabulary for grade level
* @param {string} text - Text to adapt
* @param {string} vocabularyLevel - Target vocabulary level
* @returns {string} Adapted text
*/
adaptVocabulary(text, vocabularyLevel) {
if (!text || !this.config.adaptVocabulary) return text;
let adapted = text;
const mappings = this.vocabularyMappings[vocabularyLevel];
if (mappings) {
Object.entries(mappings).forEach(([complex, simple]) => {
const regex = new RegExp(`\\b${complex}\\b`, 'gi');
adapted = adapted.replace(regex, simple);
});
}
return adapted;
}
/**
* Adapt question length
* @param {string} question - Question text
* @param {string} lengthLevel - Target length level
* @returns {string} Adapted question
*/
adaptQuestionLength(question, lengthLevel) {
if (!question) return question;
switch (lengthLevel) {
case 'short':
return this.simplifyQuestion(question);
case 'medium':
return this.moderateQuestion(question);
case 'detailed':
return this.elaborateQuestion(question);
case 'complex':
return this.complexifyQuestion(question);
default:
return question;
}
}
/**
* Adapt sentence structure
* @param {string} text - Text to adapt
* @param {string} structureLevel - Target structure level
* @returns {string} Adapted text
*/
adaptSentenceStructure(text, structureLevel) {
if (!text) return text;
switch (structureLevel) {
case 'simple':
return this.simplifyStructure(text);
case 'compound':
return this.createCompoundStructure(text);
case 'complex':
return this.createComplexStructure(text);
case 'sophisticated':
return this.createSophisticatedStructure(text);
default:
return text;
}
}
/**
* Adapt concept depth
* @param {string} content - Content to adapt
* @param {string} depthLevel - Target depth level
* @returns {string} Adapted content
*/
adaptConceptDepth(content, depthLevel) {
if (!content) return content;
switch (depthLevel) {
case 'basic':
return this.simplifyConceptDepth(content);
case 'moderate':
return this.moderateConceptDepth(content);
case 'comprehensive':
return this.comprehensiveConceptDepth(content);
case 'expert':
return this.expertConceptDepth(content);
default:
return content;
}
}
/**
* Select best distractors based on complexity
* @param {Array} distractors - Available distractors
* @param {number} count - Number to select
* @param {string} complexityLevel - Target complexity
* @returns {Array} Selected distractors
*/
selectBestDistractors(distractors, count, complexityLevel) {
if (distractors.length <= count) return distractors;
// Score distractors by complexity appropriateness
const scored = distractors.map(distractor => ({
text: distractor,
score: this.scoreDistractorComplexity(distractor, complexityLevel)
}));
// Sort by score and take top count
scored.sort((a, b) => b.score - a.score);
return scored.slice(0, count).map(item => item.text);
}
/**
* Score distractor complexity
* @param {string} distractor - Distractor text
* @param {string} complexityLevel - Target complexity
* @returns {number} Complexity score
*/
scoreDistractorComplexity(distractor, complexityLevel) {
let score = 0;
// Basic scoring based on length and vocabulary
const words = distractor.split(' ');
const avgWordLength = words.reduce((sum, word) => sum + word.length, 0) / words.length;
switch (complexityLevel) {
case 'obvious':
score = Math.max(0, 100 - avgWordLength * 10); // Prefer simpler
break;
case 'plausible':
score = 100 - Math.abs(avgWordLength - 6) * 10; // Prefer medium complexity
break;
case 'challenging':
score = avgWordLength * 10; // Prefer more complex
break;
case 'expert':
score = avgWordLength * 15 + words.length * 5; // Prefer complex and long
break;
}
return Math.max(0, Math.min(100, score));
}
/**
* Get maximum elements for cognitive load
* @param {string} cognitiveLoad - Cognitive load level
* @returns {number} Maximum elements
*/
getMaxElementsForCognitiveLoad(cognitiveLoad) {
const limits = {
'low': 2,
'medium': 3,
'high': 4,
'very-high': 5
};
return limits[cognitiveLoad] || 3;
}
/**
* Get maximum pairs for cognitive load
* @param {string} cognitiveLoad - Cognitive load level
* @returns {number} Maximum pairs
*/
getMaxPairsForCognitiveLoad(cognitiveLoad) {
const limits = {
'low': 3,
'medium': 4,
'high': 5,
'very-high': 6
};
return limits[cognitiveLoad] || 4;
}
// Text adaptation helper methods
simplifyQuestion(question) {
return question.replace(/,.*?(?=\?|$)/, ''); // Remove complex clauses
}
moderateQuestion(question) {
return question; // Keep as is for moderate complexity
}
elaborateQuestion(question) {
return question + ' Justifique sua resposta.';
}
complexifyQuestion(question) {
return question + ' Analise criticamente e considere múltiplas perspectivas.';
}
simplifyStructure(text) {
return text.replace(/;/g, '.').replace(/,.*?,/g, ','); // Break complex sentences
}
createCompoundStructure(text) {
return text; // Keep moderate complexity
}
createComplexStructure(text) {
return text; // Maintain original complexity
}
createSophisticatedStructure(text) {
return text; // Keep sophisticated structure
}
simplifyConceptDepth(content) {
return content.replace(/\b(processo|metodologia|estratégia)\b/gi, 'forma');
}
moderateConceptDepth(content) {
return content;
}
comprehensiveConceptDepth(content) {
return content;
}
expertConceptDepth(content) {
return content;
}
generateAdditionalHint(answer, framework) {
return `A resposta tem ${answer.length} letras.`;
}
/**
* Initialize vocabulary mappings for different levels
* @returns {Object} Vocabulary mappings
*/
initializeVocabularyMappings() {
return {
'simple': {
'compreender': 'entender',
'analisar': 'estudar',
'metodologia': 'forma',
'estratégia': 'plano',
'processo': 'forma',
'conceito': 'ideia',
'fundamental': 'importante',
'significativo': 'importante',
'característica': 'qualidade',
'propriedade': 'qualidade'
},
'intermediate': {
'metodologia': 'método',
'estratégia': 'estratégia',
'fundamental': 'básico',
'significativo': 'importante'
},
'advanced': {
// Keep most complex terms
},
'professional': {
// Keep all technical terms
}
};
}
/**
* Calculate adaptation statistics
* @param {Object} originalAssessment - Original assessment
* @param {Object} adaptedAssessment - Adapted assessment
* @returns {Object} Adaptation statistics
*/
calculateAdaptationStatistics(originalAssessment, adaptedAssessment) {
const stats = {
vocabularyChanges: 0,
structureChanges: 0,
questionsFiltered: 0,
optionsReduced: 0,
complexityReduction: 0
};
// Compare and calculate changes
// This would be implemented based on specific tracking needs
return stats;
}
}
// Factory function for creating ComplexityAdapter instances
export function createComplexityAdapter(config = {}) {
return new ComplexityAdapter(config);
}