educational-image-mapper.ts•15.8 kB
/**
* Educational Image Mapper
* Task 3.2: Context-Aware Image Selection Implementation
*
* Maps educational content to appropriate images for Composer widgets
* Integrates with Phase 2 assessment components and content generation system
*/
import { ImageSelectionService, ImageSelection } from '../services/image-selection-service';
interface WidgetImageMapping {
widget_type: string;
image_properties: {
[key: string]: ImageSelection;
};
educational_metadata: {
subject: string;
topic: string;
grade_level: string;
learning_objective: string;
};
}
interface EducationalImageContext {
content: string;
subject: string;
grade_level: string;
widget_type: string;
learning_intent: string;
assessment_type?: string;
}
export class EducationalImageMapper {
private imageService: ImageSelectionService;
constructor() {
this.imageService = new ImageSelectionService();
}
/**
* Map educational content to widget-specific images
*/
public mapContentToImages(context: EducationalImageContext): WidgetImageMapping {
const subject = this.normalizeSubject(context.subject);
switch (context.widget_type) {
case 'head-1':
return this.mapHeaderImages(context, subject);
case 'text-1':
return this.mapTextImages(context, subject);
case 'quiz-1':
return this.mapQuizImages(context, subject);
case 'flashcards-1':
return this.mapFlashcardImages(context, subject);
case 'gallery-1':
return this.mapGalleryImages(context, subject);
case 'hotspots-1':
return this.mapHotspotImages(context, subject);
case 'image-1':
return this.mapStandaloneImages(context, subject);
default:
return this.mapGenericImages(context, subject);
}
}
/**
* Map header widget images (head-1)
*/
private mapHeaderImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const headerImage = this.imageService.getWidgetSpecificImage('head-1', context.content, subject);
const avatarImage = this.generateEducatorAvatar(subject, context.grade_level);
return {
widget_type: 'head-1',
image_properties: {
background_image: headerImage,
avatar: avatarImage
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Apresentar o tema da aula de forma visual e atrativa'
}
};
}
/**
* Map text widget images (text-1)
*/
private mapTextImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const contextualImage = this.imageService.selectContextualImage(
context.content,
subject,
context.grade_level,
'text-1'
);
return {
widget_type: 'text-1',
image_properties: {
inline_image: contextualImage
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Ilustrar conceitos apresentados no texto'
}
};
}
/**
* Map quiz widget images (quiz-1)
* Integrates with Phase 2 QuizGenerator component
*/
private mapQuizImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const questionImages = this.generateQuizQuestionImages(context.content, subject);
const feedbackImages = this.generateQuizFeedbackImages(subject, context.grade_level);
return {
widget_type: 'quiz-1',
image_properties: {
question_images: questionImages,
feedback_correct: feedbackImages.correct,
feedback_incorrect: feedbackImages.incorrect
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Avaliar compreensão através de questões visuais'
}
};
}
/**
* Map flashcard widget images (flashcards-1)
* Integrates with Phase 2 FlashcardGenerator component
*/
private mapFlashcardImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const cardImages = this.generateFlashcardImages(context.content, subject, context.grade_level);
return {
widget_type: 'flashcards-1',
image_properties: {
card_images: cardImages
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Reforçar aprendizado através de cartões visuais'
}
};
}
/**
* Map gallery widget images (gallery-1)
*/
private mapGalleryImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const galleryImages = this.generateGallerySequence(context.content, subject, context.grade_level);
return {
widget_type: 'gallery-1',
image_properties: {
slides: galleryImages
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Apresentar sequência visual do processo educativo'
}
};
}
/**
* Map hotspot widget images (hotspots-1)
*/
private mapHotspotImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const backgroundImage = this.imageService.getWidgetSpecificImage('hotspots-1', context.content, subject);
const hotspotMarkers = this.generateHotspotMarkers(context.content, subject);
return {
widget_type: 'hotspots-1',
image_properties: {
background_image: backgroundImage,
hotspot_markers: hotspotMarkers
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Explorar conteúdo através de interação visual'
}
};
}
/**
* Map standalone image widget (image-1)
*/
private mapStandaloneImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const primaryImage = this.imageService.selectContextualImage(
context.content,
subject,
context.grade_level,
'image-1'
);
return {
widget_type: 'image-1',
image_properties: {
primary_image: primaryImage
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Apresentar conceito visual principal'
}
};
}
/**
* Generic image mapping for unknown widget types
*/
private mapGenericImages(context: EducationalImageContext, subject: string): WidgetImageMapping {
const fallbackImage = this.imageService.selectContextualImage(
context.content,
subject,
context.grade_level
);
return {
widget_type: context.widget_type,
image_properties: {
generic_image: fallbackImage
},
educational_metadata: {
subject,
topic: this.extractTopic(context.content),
grade_level: context.grade_level,
learning_objective: 'Suporte visual genérico para conteúdo'
}
};
}
/**
* Generate educator avatar based on subject and grade level
*/
private generateEducatorAvatar(subject: string, gradeLevel: string): ImageSelection {
const avatarStyles = {
'fundamental': 'friendly',
'médio': 'professional',
'superior': 'academic'
};
const style = avatarStyles[gradeLevel as keyof typeof avatarStyles] || 'friendly';
const subjectIcon = this.getSubjectIcon(subject);
return {
url: `https://ui-avatars.com/api/?name=Professor&background=2563eb&color=ffffff&size=120&format=png&font-size=0.33&length=1&rounded=true`,
caption: `Professor de ${subject}`,
alt_text: `Avatar do professor de ${subject}`,
educational_context: `${subject}/avatar/${gradeLevel}`,
fallback_used: false
};
}
/**
* Generate quiz question images
*/
private generateQuizQuestionImages(content: string, subject: string): { [questionId: string]: ImageSelection } {
// Extract key concepts that could benefit from images
const concepts = this.extractVisualConcepts(content, subject);
const questionImages: { [questionId: string]: ImageSelection } = {};
concepts.forEach((concept, index) => {
const questionId = `question_${index + 1}`;
questionImages[questionId] = this.imageService.selectContextualImage(
concept,
subject,
'fundamental',
'quiz-1'
);
});
return questionImages;
}
/**
* Generate quiz feedback images
*/
private generateQuizFeedbackImages(subject: string, gradeLevel: string): { correct: ImageSelection; incorrect: ImageSelection } {
return {
correct: {
url: 'https://cdn.digitalpages.com.br/education/feedback/success-celebration.jpg',
caption: 'Parabéns! Resposta correta!',
alt_text: 'Imagem de sucesso para resposta correta',
educational_context: `${subject}/feedback/correct`,
fallback_used: false
},
incorrect: {
url: 'https://cdn.digitalpages.com.br/education/feedback/encouragement-try-again.jpg',
caption: 'Não desanime! Tente novamente.',
alt_text: 'Imagem de encorajamento para resposta incorreta',
educational_context: `${subject}/feedback/incorrect`,
fallback_used: false
}
};
}
/**
* Generate flashcard images
*/
private generateFlashcardImages(content: string, subject: string, gradeLevel: string): { [cardId: string]: { front: ImageSelection; back: ImageSelection } } {
const keyTerms = this.extractKeyTerms(content, subject);
const cardImages: { [cardId: string]: { front: ImageSelection; back: ImageSelection } } = {};
keyTerms.forEach((term, index) => {
const cardId = `card_${index + 1}`;
// Front image: Visual representation of the term
cardImages[cardId] = {
front: this.imageService.selectContextualImage(
term.definition,
subject,
gradeLevel,
'flashcards-1'
),
back: {
url: 'https://cdn.digitalpages.com.br/education/flashcards/definition-background.jpg',
caption: `Definição: ${term.term}`,
alt_text: `Imagem de fundo para definição de ${term.term}`,
educational_context: `${subject}/flashcard/definition`,
fallback_used: false
}
};
});
return cardImages;
}
/**
* Generate gallery image sequence
*/
private generateGallerySequence(content: string, subject: string, gradeLevel: string): ImageSelection[] {
const steps = this.extractProcessSteps(content, subject);
return steps.map((step, index) =>
this.imageService.selectContextualImage(
step,
subject,
gradeLevel,
'gallery-1'
)
);
}
/**
* Generate hotspot markers
*/
private generateHotspotMarkers(content: string, subject: string): { [markerId: string]: ImageSelection } {
const interactiveElements = this.extractInteractiveElements(content, subject);
const markers: { [markerId: string]: ImageSelection } = {};
interactiveElements.forEach((element, index) => {
const markerId = `marker_${index + 1}`;
markers[markerId] = {
url: 'https://cdn.digitalpages.com.br/education/hotspots/interactive-marker.png',
caption: element.description,
alt_text: `Marcador interativo: ${element.name}`,
educational_context: `${subject}/hotspot/marker`,
fallback_used: false
};
});
return markers;
}
/**
* Helper methods for content analysis
*/
private normalizeSubject(subject: string): string {
const subjectMapping: { [key: string]: string } = {
'science': 'ciências',
'sciences': 'ciências',
'physics': 'física',
'chemistry': 'química',
'history': 'história',
'mathematics': 'matemática',
'math': 'matemática',
'portuguese': 'português',
'literature': 'português'
};
return subjectMapping[subject.toLowerCase()] || subject.toLowerCase();
}
private extractTopic(content: string): string {
// Simple topic extraction - could be enhanced with NLP
const words = content.toLowerCase().split(' ');
const stopWords = ['a', 'an', 'the', 'is', 'are', 'of', 'in', 'on', 'at', 'to', 'for', 'with', 'by'];
const significantWords = words.filter(word => !stopWords.includes(word) && word.length > 3);
return significantWords.slice(0, 3).join(' ');
}
private getSubjectIcon(subject: string): string {
const icons: { [key: string]: string } = {
'ciências': '🔬',
'física': '⚡',
'química': '🧪',
'história': '📚',
'matemática': '📐',
'português': '📖'
};
return icons[subject] || '🎓';
}
private extractVisualConcepts(content: string, subject: string): string[] {
// Extract concepts that would benefit from visual representation
// This is a simplified implementation - could be enhanced with ML
const sentences = content.split('.').filter(s => s.trim().length > 10);
return sentences.slice(0, 3); // Take first 3 sentences as visual concepts
}
private extractKeyTerms(content: string, subject: string): { term: string; definition: string }[] {
// Extract key terms and their definitions
// Simplified implementation - could use NLP for better extraction
const sentences = content.split('.').filter(s => s.trim().length > 10);
return sentences.slice(0, 5).map((sentence, index) => ({
term: `Conceito ${index + 1}`,
definition: sentence.trim()
}));
}
private extractProcessSteps(content: string, subject: string): string[] {
// Extract sequential steps from educational content
const sentences = content.split('.').filter(s => s.trim().length > 10);
return sentences.slice(0, 4); // Take up to 4 steps for gallery
}
private extractInteractiveElements(content: string, subject: string): { name: string; description: string }[] {
// Extract elements suitable for interactive hotspots
return [
{ name: 'Elemento 1', description: 'Ponto de interesse principal' },
{ name: 'Elemento 2', description: 'Componente secundário' },
{ name: 'Elemento 3', description: 'Detalhe importante' }
];
}
/**
* Integration with Phase 2 Assessment Components
*/
public integrateWithAssessmentEngine(assessmentData: any, subject: string): WidgetImageMapping[] {
const mappings: WidgetImageMapping[] = [];
// Map quiz images
if (assessmentData.quiz) {
const quizContext: EducationalImageContext = {
content: assessmentData.quiz.metadata.topic,
subject,
grade_level: assessmentData.quiz.metadata.gradeLevel,
widget_type: 'quiz-1',
learning_intent: 'assessment',
assessment_type: 'quiz'
};
mappings.push(this.mapContentToImages(quizContext));
}
// Map flashcard images
if (assessmentData.flashcards) {
const flashcardContext: EducationalImageContext = {
content: assessmentData.flashcards.join(' '),
subject,
grade_level: assessmentData.metadata?.gradeLevel || 'fundamental',
widget_type: 'flashcards-1',
learning_intent: 'memorization',
assessment_type: 'flashcards'
};
mappings.push(this.mapContentToImages(flashcardContext));
}
return mappings;
}
}