UNPKG

education-module-ai

Version:
588 lines (587 loc) 24.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EducationalContentController = void 0; const agent_framework_backend_1 = require("@defikitdotnet/agent-framework-backend"); const uuid_1 = require("uuid"); const EducationalContent_1 = require("../models/EducationalContent"); const plugin_manager_1 = require("../plugins/plugin-manager"); const ai_service_1 = require("../services/ai-service"); // In-memory storage for demo purposes const contentStore = {}; // Create a resource implementation for EducationalContentResource const educationalContentResource = { name: "educational-contents", findAll: async () => Object.values(contentStore), findById: async (id) => contentStore[id] || null, create: async (data) => { const id = (0, uuid_1.v4)(); const newContent = new EducationalContent_1.EducationalContentResource(); Object.assign(newContent, data, { id, createdAt: new Date(), }); contentStore[id] = newContent; return newContent; }, update: async (id, data) => { if (!contentStore[id]) return null; const updated = { ...contentStore[id], ...data, updatedAt: new Date() }; contentStore[id] = updated; return updated; }, delete: async (id) => { if (!contentStore[id]) return false; delete contentStore[id]; return true; }, findBySubject: async (subject) => { return Object.values(contentStore).filter((content) => content.subject === subject); }, findByLevel: async (level) => { return Object.values(contentStore).filter((content) => content.level === level); }, search: async (query) => { const lowerQuery = query.toLowerCase(); return Object.values(contentStore).filter((content) => content.title.toLowerCase().includes(lowerQuery) || content.content.toLowerCase().includes(lowerQuery) || content.keywords.some((keyword) => keyword.toLowerCase().includes(lowerQuery))); }, }; /** * Educational Content API controller */ let EducationalContentController = class EducationalContentController extends agent_framework_backend_1.ResourceController { constructor() { super(educationalContentResource); this.educationModuleAdapter = null; this.configService = EducationalContent_1.EducationConfigService.getInstance(); console.log("EducationalContentController initialized"); } setEducationModuleAdapter(adapter) { this.educationModuleAdapter = adapter; console.log("EducationModuleAdapter set in controller"); } /** * Get all educational content items */ async findAll(req, res) { try { let items; if (this.educationModuleAdapter) { // Use database adapter if available items = await this.educationModuleAdapter.getEducationalContents(); } else { // Fall back to in-memory storage items = await this.resource.findAll(); } res.status(200).json({ data: items }); } catch (error) { console.error("Error retrieving educational content list:", error); res.status(500).json({ error: "Failed to retrieve educational content list" }); } } /** * Create educational content using database adapter */ async create(req, res) { try { console.log("Creating educational content with payload:", JSON.stringify(req.body, null, 2)); // Validate subject and level from configuration const subject = this.validateSubject(req.body.subject); const level = this.validateEducationalLevel(req.body.level); console.log("Validation results:", { subject: subject || "Invalid", level: level || "Invalid", validSubjects: this.configService.subjects, validLevels: this.configService.levels }); if (!subject) { res.status(400).json({ error: `Invalid subject. Valid subjects are: ${this.configService.subjects.join(', ')}` }); return; } if (!level) { res.status(400).json({ error: `Invalid educational level. Valid levels are: ${this.configService.levels.join(', ')}` }); return; } let newContent; if (this.educationModuleAdapter) { console.log("Using database adapter to create content"); try { // Create content using database adapter const id = (0, uuid_1.v4)(); const now = new Date(); // Ensure we have proper JavaScript Date objects, not serialized dates const content = { id, title: req.body.title, content: req.body.content, type: req.body.type, subject, level, keywords: req.body.keywords || [], createdAt: now, updatedAt: now }; console.log("Prepared content for database:", content); const contentId = await this.educationModuleAdapter.createEducationalContent(content); console.log("Content created successfully with ID:", contentId); // Fetch the complete content after creation to return in response newContent = await this.educationModuleAdapter.getEducationalContent(contentId); if (!newContent) { // If content can't be fetched, create a basic response newContent = { id: contentId, title: content.title, content: content.content, type: content.type, subject: content.subject, level: content.level, keywords: content.keywords, createdAt: content.createdAt, updatedAt: content.updatedAt }; } } catch (adapterError) { console.error("Error in database adapter:", adapterError); throw adapterError; } } else { console.log("Using in-memory storage (adapter not available)"); // Fall back to in-memory storage const content = { ...req.body, subject, level }; newContent = await this.resource.create(content); } // Notify plugins await plugin_manager_1.pluginManager.notifyContentCreated(newContent); res.status(201).json({ data: newContent }); } catch (error) { console.error("Error creating educational content:", error); res.status(500).json({ error: "Failed to create educational content", details: String(error) }); } } /** * Update educational content using database adapter */ async update(req, res) { try { // Validate subject and level from configuration if provided in the update let subject = req.body.subject; let level = req.body.level; if (subject) { subject = this.validateSubject(subject); if (!subject) { res.status(400).json({ error: `Invalid subject. Valid subjects are: ${this.configService.subjects.join(', ')}` }); return; } } if (level) { level = this.validateEducationalLevel(level); if (!level) { res.status(400).json({ error: `Invalid educational level. Valid levels are: ${this.configService.levels.join(', ')}` }); return; } } let updatedContent; if (this.educationModuleAdapter) { // Update content using database adapter const content = { id: req.params.id, title: req.body.title, content: req.body.content, type: req.body.type, subject: subject || req.body.subject, level: level || req.body.level, keywords: req.body.keywords, updatedAt: new Date().toISOString() }; updatedContent = await this.educationModuleAdapter.updateEducationalContent(content); } else { // Fall back to in-memory storage const updateData = { ...req.body }; if (subject) updateData.subject = subject; if (level) updateData.level = level; updatedContent = await this.resource.update(req.params.id, updateData); } // Notify plugins await plugin_manager_1.pluginManager.notifyContentUpdated(updatedContent); res.status(200).json({ data: updatedContent }); } catch (error) { console.error("Error updating educational content:", error); res.status(500).json({ error: "Failed to update educational content" }); } } /** * Delete educational content using database adapter */ async delete(req, res) { try { let deleted; if (this.educationModuleAdapter) { // Delete content using database adapter deleted = await this.educationModuleAdapter.deleteEducationalContent(req.params.id); } else { // Fall back to in-memory storage deleted = await this.resource.delete(req.params.id); } // Notify plugins await plugin_manager_1.pluginManager.notifyContentDeleted(deleted); res.status(200).json({ data: deleted }); } catch (error) { console.error("Error deleting educational content:", error); res.status(500).json({ error: "Failed to delete educational content" }); } } /** * Get educational content by ID using database adapter */ async getById(req, res) { try { let content; if (this.educationModuleAdapter) { // Get content using database adapter content = await this.educationModuleAdapter.getEducationalContent(req.params.id); } else { // Fall back to in-memory storage content = await this.resource.findById(req.params.id); } res.status(200).json({ data: content }); } catch (error) { console.error("Error retrieving educational content by ID:", error); res.status(500).json({ error: "Failed to retrieve educational content by ID" }); } } /** * Generate educational content */ async generateContent(req, res) { const { subject, level, type, topic } = req.body; if (!subject || !level || !type || !topic) { res.status(400).json({ error: "Missing required fields" }); return; } try { // Validate parameters if (!this.isValidSubject(subject)) { res.status(400).json({ error: "Invalid subject" }); return; } if (!this.isValidLevel(level)) { res.status(400).json({ error: "Invalid educational level" }); return; } if (!this.isValidContentType(type)) { res.status(400).json({ error: "Invalid content type" }); return; } // Generate content using AI service const generatedContent = await ai_service_1.aiService.generateEducationalContent(subject, level, type, topic); // Create and save the generated content const newContent = await this.resource.create({ title: generatedContent.title, content: generatedContent.content, type: type, subject: subject, level: level, keywords: generatedContent.keywords, }); // Notify plugins await plugin_manager_1.pluginManager.notifyContentCreated(newContent); res.status(201).json({ data: newContent }); } catch (error) { console.error("Error generating educational content:", error); res.status(500).json({ error: "Failed to generate educational content" }); } } /** * Generate quiz */ async generateQuiz(req, res) { console.log("Quiz endpoint called with data:", req.body); const { subject, level, topic, questionCount } = req.body; if (!subject || !level || !topic) { res.status(400).json({ error: "Subject, level, and topic are required" }); return; } try { const validSubject = this.validateSubject(subject); const validLevel = this.validateEducationalLevel(level); if (!validSubject) { res.status(400).json({ error: `Invalid subject: ${subject}` }); return; } if (!validLevel) { res.status(400).json({ error: `Invalid educational level: ${level}` }); return; } console.log(`Generating quiz for topic: "${topic}" in ${subject} at ${level} level with ${questionCount || 5} questions`); // Generate quiz using AI service const quiz = await ai_service_1.aiService.generateQuiz(validSubject, validLevel, topic, questionCount || 5); // Create a content entry for this quiz const quizContent = await this.resource.create({ title: quiz.title, content: JSON.stringify(quiz.questions), subject: validSubject, level: validLevel, type: "quiz", keywords: [topic, "quiz", validSubject, validLevel], }); // Notify plugins await plugin_manager_1.pluginManager.notifyContentCreated(quizContent); res.status(200).json({ data: { ...quiz, id: quizContent.id } }); } catch (error) { console.error("Error generating quiz:", error); res.status(500).json({ error: "Failed to generate quiz" }); } } /** * Question answering endpoint */ async answerQuestion(req, res) { const { subject, level, question } = req.body; if (!subject || !level || !question) { res.status(400).json({ error: "Subject, level, and question are required" }); return; } try { const validSubject = this.validateSubject(subject); const validLevel = this.validateEducationalLevel(level); if (!validSubject) { res.status(400).json({ error: `Invalid subject: ${subject}` }); return; } if (!validLevel) { res.status(400).json({ error: `Invalid educational level: ${level}` }); return; } console.log(`Answering question about ${subject} at ${level} level: "${question}"`); // Answer question using AI service const answer = await ai_service_1.aiService.answerQuestion(validSubject, validLevel, question); res.status(200).json({ data: answer }); } catch (error) { console.error("Error answering question:", error); res.status(500).json({ error: "Failed to answer question" }); } } /** * Search for educational content */ async searchContent(req, res) { const { q } = req.query; if (!q || typeof q !== "string") { res.status(400).json({ error: "Missing search query" }); return; } try { const results = await educationalContentResource.search(q); res.status(200).json({ data: results }); } catch (error) { console.error("Error searching content:", error); res.status(500).json({ error: "Failed to search content" }); } } /** * Get content by subject */ async getBySubject(req, res) { const { subject } = req.params; if (!subject || !this.isValidSubject(subject)) { res.status(400).json({ error: "Invalid subject" }); return; } try { const results = await educationalContentResource.findBySubject(subject); res.status(200).json({ data: results }); } catch (error) { console.error("Error retrieving content by subject:", error); res.status(500).json({ error: "Failed to retrieve content" }); } } /** * Get content by educational level */ async getByLevel(req, res) { const { level } = req.params; if (!level || !this.isValidLevel(level)) { res.status(400).json({ error: "Invalid educational level" }); return; } try { const results = await educationalContentResource.findByLevel(level); res.status(200).json({ data: results }); } catch (error) { console.error("Error retrieving content by level:", error); res.status(500).json({ error: "Failed to retrieve content" }); } } /** * Get available configuration options */ async getConfig(req, res) { try { res.status(200).json({ data: { levels: this.configService.levels, subjects: this.configService.subjects } }); } catch (error) { console.error("Error retrieving configuration:", error); res.status(500).json({ error: "Failed to retrieve configuration" }); } } /** * Check if a subject is valid according to configuration */ isValidSubject(subject) { return this.configService.isValidSubject(subject); } /** * Check if an educational level is valid according to configuration */ isValidLevel(level) { return this.configService.isValidLevel(level); } /** * Check if content type is valid */ isValidContentType(type) { return ["explanation", "tutorial", "practice_problem", "quiz", "reference_material"].includes(type); } /** * Validate a subject value */ validateSubject(subject) { return this.isValidSubject(subject) ? subject : null; } /** * Validate an educational level value */ validateEducationalLevel(level) { return this.isValidLevel(level) ? level : null; } }; exports.EducationalContentController = EducationalContentController; __decorate([ (0, agent_framework_backend_1.Get)("/"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "findAll", null); __decorate([ (0, agent_framework_backend_1.Post)("/"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "create", null); __decorate([ (0, agent_framework_backend_1.Put)("/:id"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "update", null); __decorate([ (0, agent_framework_backend_1.Delete)("/:id"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "delete", null); __decorate([ (0, agent_framework_backend_1.Get)("/:id"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "getById", null); __decorate([ (0, agent_framework_backend_1.Post)("/generate"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "generateContent", null); __decorate([ (0, agent_framework_backend_1.Post)("/quiz"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "generateQuiz", null); __decorate([ (0, agent_framework_backend_1.Post)("/answer-question"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "answerQuestion", null); __decorate([ (0, agent_framework_backend_1.Get)("/search"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "searchContent", null); __decorate([ (0, agent_framework_backend_1.Get)("/subject/:subject"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "getBySubject", null); __decorate([ (0, agent_framework_backend_1.Get)("/level/:level"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "getByLevel", null); __decorate([ (0, agent_framework_backend_1.Get)("/config"), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], EducationalContentController.prototype, "getConfig", null); exports.EducationalContentController = EducationalContentController = __decorate([ (0, agent_framework_backend_1.Controller)("/api/educational-contents"), __metadata("design:paramtypes", []) ], EducationalContentController);