brazilian-educational-analyzer.ts•10.6 kB
/**
* Brazilian Educational Content Analyzer
* Semantic-first approach with Brazilian Portuguese educational context
*/
export interface BrazilianEducationalContext {
gradeLevel: string; // "fundamental-1", "fundamental-2", "medio", "superior", "tecnico"
subject?: string;
duration?: number; // minutes
pedagogicalApproach?: string;
bnccCompetencies?: string[];
}
export class BrazilianEducationalAnalyzer {
// High-confidence Brazilian educational triggers (additive, not restrictive)
private brazilianEducationalBoosts = [
// Grade levels
/\b(?:ensino\s+)?fundamental\s*[12]?\b/i,
/\b(?:ensino\s+)?m[eé]dio\b/i,
/\b(?:ensino\s+)?superior\b/i,
/\b(?:ensino\s+)?t[eé]cnico\b/i,
/\b(?:[1-9]º|primeiro|segundo|terceiro|quarto|quinto|sexto|s[eé]timo|oitavo|nono)\s+ano\b/i,
// Time/duration
/\bcarga\s+hor[aá]ria\b/i,
/\bminutos?\s+de\s+aula\b/i,
/\bper[ií]odo\s+(?:letivo|escolar)\b/i,
/\bbimestre|trimestre|semestre\b/i,
// Educational activities
/\batividade\s+(?:pedag[oó]gica|educacional|escolar)\b/i,
/\bsequência\s+did[aá]tica\b/i,
/\bplano\s+de\s+aula\b/i,
/\bobjetivos?\s+de\s+aprendizagem\b/i,
/\bcompetências\s+e\s+habilidades\b/i,
// Assessment (Brazilian context)
/\bavalia[çc][aã]o\s+(?:diagn[oó]stica|formativa|somativa)\b/i,
/\bquest[oõ]es?\s+de\s+(?:vestibular|enem|concurso)\b/i,
/\bprova|teste|exerc[ií]cios?\b/i,
// BNCC and curriculum
/\bbncc|base\s+nacional\s+comum\s+curricular\b/i,
/\bcurr[ií]culo\s+(?:escolar|nacional|estadual)\b/i,
/\bpar[aâ]metros?\s+curriculares\b/i,
];
private gradeMapping = {
// Ensino Fundamental I (1º ao 5º ano)
'fundamental-1': ['primeiro', 'segundo', 'terceiro', 'quarto', 'quinto', '1º', '2º', '3º', '4º', '5º'],
// Ensino Fundamental II (6º ao 9º ano)
'fundamental-2': ['sexto', 'sétimo', 'oitavo', 'nono', '6º', '7º', '8º', '9º'],
// Ensino Médio (1º ao 3º ano)
'medio': ['primeiro.*médio', 'segundo.*médio', 'terceiro.*médio', '1º.*médio', '2º.*médio', '3º.*médio'],
};
private subjectKeywords = {
'matemática': ['matemática', 'álgebra', 'geometria', 'cálculo', 'estatística', 'números'],
'português': ['português', 'língua portuguesa', 'literatura', 'gramática', 'redação', 'texto'],
'ciências': ['ciências', 'biologia', 'física', 'química', 'natureza', 'experimento'],
'história': ['história', 'histórico', 'passado', 'civilização', 'época', 'período'],
'geografia': ['geografia', 'território', 'região', 'clima', 'relevo', 'população'],
'inglês': ['inglês', 'english', 'língua inglesa', 'vocabulary', 'grammar'],
'educação física': ['educação física', 'esporte', 'atividade física', 'corpo', 'movimento'],
'artes': ['artes', 'arte', 'pintura', 'música', 'teatro', 'dança', 'cultura'],
};
public analyzeBrazilianContext(content: string, title?: string): BrazilianEducationalContext {
const fullText = `${title || ''} ${content}`.toLowerCase();
return {
gradeLevel: this.detectGradeLevel(fullText),
subject: this.detectSubject(fullText),
duration: this.extractDuration(fullText),
pedagogicalApproach: this.detectPedagogicalApproach(fullText),
bnccCompetencies: this.extractBNCC(fullText),
};
}
public calculateEducationalConfidence(content: string, title?: string): number {
const fullText = `${title || ''} ${content}`.toLowerCase();
let confidence = 0.3; // Base semantic confidence
// Add confidence boosts from Brazilian educational patterns
this.brazilianEducationalBoosts.forEach(pattern => {
const matches = fullText.match(pattern);
if (matches) {
confidence += 0.1 * matches.length; // Each match adds 10% confidence
}
});
// Subject matter boost
const hasSubject = Object.values(this.subjectKeywords).some(keywords =>
keywords.some(keyword => fullText.includes(keyword))
);
if (hasSubject) confidence += 0.2;
// Grade level boost
const hasGradeLevel = Object.values(this.gradeMapping).some(grades =>
grades.some(grade => new RegExp(grade, 'i').test(fullText))
);
if (hasGradeLevel) confidence += 0.2;
// Duration/time context boost
if (this.extractDuration(fullText)) confidence += 0.1;
return Math.min(1.0, confidence);
}
public isEducationalContent(content: string, title?: string): boolean {
// Semantic-first approach: if it contains educational keywords, concepts, or structure
const confidence = this.calculateEducationalConfidence(content, title);
// Also check for semantic educational indicators
const semanticIndicators = this.checkSemanticEducationalIndicators(content, title);
return confidence > 0.4 || semanticIndicators;
}
private detectGradeLevel(text: string): string {
// Check for explicit grade mentions
for (const [level, patterns] of Object.entries(this.gradeMapping)) {
for (const pattern of patterns) {
if (new RegExp(pattern, 'i').test(text)) {
return level;
}
}
}
// Semantic detection based on content complexity and vocabulary
return this.inferGradeLevelFromContent(text);
}
private detectSubject(text: string): string | undefined {
let bestMatch = '';
let maxScore = 0;
for (const [subject, keywords] of Object.entries(this.subjectKeywords)) {
let score = 0;
keywords.forEach(keyword => {
const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
const matches = text.match(regex);
if (matches) {
score += matches.length;
}
});
if (score > maxScore) {
maxScore = score;
bestMatch = subject;
}
}
return maxScore > 0 ? bestMatch : undefined;
}
private extractDuration(text: string): number | undefined {
// Look for Brazilian duration patterns
const durationPatterns = [
/(\d+)\s*minutos?\s*(?:de\s+aula)?/i,
/(\d+)\s*horas?\s*(?:aula|atividade)?/i,
/carga\s+horária\s*(?:de\s*)?(\d+)\s*(?:minutos?|horas?)/i,
/período\s+de\s+(\d+)\s*(?:minutos?|horas?)/i,
/duração\s*(?:de\s*)?(\d+)\s*(?:minutos?|horas?)/i,
];
for (const pattern of durationPatterns) {
const match = text.match(pattern);
if (match) {
const value = parseInt(match[1]);
// Convert hours to minutes if needed
return text.includes('hora') ? value * 60 : value;
}
}
return undefined;
}
private detectPedagogicalApproach(text: string): string | undefined {
const approaches = {
'construtivista': /construtivism|construtivista|piaget|vygotsky/i,
'tradicional': /tradicional|expositiv|aula\s+magistral/i,
'ativa': /metodologia\s+ativa|aprendizagem\s+ativa|participativ/i,
'investigativa': /investigativ|pesquisa|descoberta|exploratóri/i,
'colaborativa': /colaborativ|grupo|equipe|cooperativ/i,
'lúdica': /lúdic|jogo|brincadeira|divertid/i,
};
for (const [approach, pattern] of Object.entries(approaches)) {
if (pattern.test(text)) {
return approach;
}
}
return undefined;
}
private extractBNCC(text: string): string[] {
const competencies: string[] = [];
// Look for explicit BNCC mentions
const bnccPatterns = [
/competência\s+(\d+)/gi,
/habilidade\s+(ef\d+[a-z]+\d+)/gi,
/campo\s+de\s+experiência/i,
/área\s+do\s+conhecimento/i,
];
bnccPatterns.forEach(pattern => {
const matches = text.match(pattern);
if (matches) {
competencies.push(...matches);
}
});
return competencies;
}
private checkSemanticEducationalIndicators(content: string, title?: string): boolean {
const fullText = `${title || ''} ${content}`.toLowerCase();
// Semantic indicators that suggest educational content
const semanticIndicators = [
// Learning-focused language
/\b(?:aprender|ensinar|estudar|compreender|entender)\b/,
/\b(?:explicar|demonstrar|mostrar|apresentar)\b/,
/\b(?:conhecimento|habilidade|competência|capacidade)\b/,
// Student-focused language
/\b(?:alunos?|estudantes?|discentes?)\b/,
/\b(?:turma|classe|sala\s+de\s+aula)\b/,
// Educational structure
/\b(?:objetivo|meta|finalidade)\s+(?:de\s+)?(?:aprendizagem|educacional)\b/,
/\b(?:conteúdo|matéria|disciplina|assunto)\b/,
/\b(?:exercício|atividade|tarefa|trabalho)\s+(?:escolar|acadêmic|educacional)\b/,
// Assessment language
/\b(?:avaliar|verificar|testar|medir)\s+(?:conhecimento|aprendizagem|compreensão)\b/,
/\b(?:questão|pergunta|resposta|solução)\b/,
];
const matchCount = semanticIndicators.filter(pattern => pattern.test(fullText)).length;
// If we have multiple semantic indicators, it's likely educational
return matchCount >= 2;
}
private inferGradeLevelFromContent(text: string): string {
// Simple complexity-based inference
const avgWordLength = text.split(/\s+/).reduce((sum, word) => sum + word.length, 0) / text.split(/\s+/).length;
const sentenceCount = text.split(/[.!?]+/).length;
const avgSentenceLength = text.split(/\s+/).length / sentenceCount;
// Basic heuristics (can be refined)
if (avgWordLength < 5 && avgSentenceLength < 10) {
return 'fundamental-1';
} else if (avgWordLength < 6 && avgSentenceLength < 15) {
return 'fundamental-2';
} else if (avgWordLength < 7 && avgSentenceLength < 20) {
return 'medio';
} else {
return 'superior';
}
}
// Map Brazilian grade levels to international equivalents for the main system
public mapToInternationalAudience(gradeLevel: string): 'elementary' | 'middle' | 'high' | 'college' | 'adult' | 'professional' {
const mapping = {
'fundamental-1': 'elementary',
'fundamental-2': 'middle',
'medio': 'high',
'superior': 'college',
'tecnico': 'professional',
};
return (mapping[gradeLevel as keyof typeof mapping] || 'adult') as 'elementary' | 'middle' | 'high' | 'college' | 'adult' | 'professional';
}
// Convert Brazilian duration to international complexity
public mapComplexityFromDuration(duration?: number): 'basic' | 'intermediate' | 'advanced' {
if (!duration) return 'intermediate';
if (duration <= 20) return 'basic';
if (duration <= 40) return 'intermediate';
return 'advanced';
}
}