Skip to main content
Glama
educational-image-mapper.ts15.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; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server