diff --git a/questionsservice/questiongeneratorservice/questiongenerator-service.js b/questionsservice/questiongeneratorservice/questiongenerator-service.js index ca384971..21c8721d 100644 --- a/questionsservice/questiongeneratorservice/questiongenerator-service.js +++ b/questionsservice/questiongeneratorservice/questiongenerator-service.js @@ -2,7 +2,7 @@ const express = require('express'); const cors = require('cors'); const axios = require('axios'); const mongoose = require('mongoose'); -const { Pais } = require('./questiongenerator-model') +const { QuestionGenerator } = require('./questiongenerator') const app = express(); const port = 8007; @@ -22,69 +22,47 @@ app.use(express.json()); // Middleware to enable CORS (cross-origin resource sharing). In order for the API to be accessible by other origins (domains). app.use(cors()); -function shuffle(array) { - let currentIndex = array.length; - let randomIndex; - // Mientras queden elementos para mezclar. - while (currentIndex > 0) { - // Escoge un elemento aleatorio. - randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; - // Intercambia el elemento actual con el elemento aleatorio. - [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]]; - } - return array; -} +// Only parse query parameters into strings, not objects (adds security) +app.set('query parser', 'simple'); -async function generateQuestion() { - var elementos = await Pais.find({ capital: { $exists: true } }).exec(); - console.log("Find:\n" + elementos) - elementos = shuffle(elementos); - var mockedQuestions = [{ - pregunta: "¿Cual es la capital de " + elementos[0].pais + "?", - respuesta_correcta: elementos[0].capital, - respuestas_incorrectas: [elementos[1].capital, elementos[2].capital, elementos[3].capital] - }]; - console.log(mockedQuestions); - return mockedQuestions +function validateNumberInQuery(number, minValue, paramName, defValue) { + if (!(paramName in number)) return defValue; + n = Number(number[paramName]); + if (isNaN(n)) throw new Error(`A number was expected in param \'${paramName}\'`); + if (n < minValue) throw new Error(`\'${paramName}\' must be at least \'${minValue}\'`); + return n; } -// Function to generate the required number of questions -async function getQuestions(req) { - const { n_preguntas, n_respuestas, tema } = req.query; - var preguntas = Number(n_preguntas); - var respuestas = Number(n_respuestas); - var temas = String(tema); - - // if (isNaN(preguntas)) { - // generateQuestion() - // console.log("merda", mockedQuestions) - // return mockedQuestions.slice(0, 4); - // } - // const response = []; - // generateQuestion(); - // for (let i = 0; i < preguntas; i++) { - // response.push(mockedQuestions[i % 11]); - // } - return await generateQuestion(); +// Function to validate required fields in the request body +function validateFields(query) { + const preguntas = validateNumberInQuery(query, 1, 'n_preguntas', 1); + const respuestas = validateNumberInQuery(query, 1, 'n_respuestas', 4); + var temas = query.tema || []; + if (!Array.isArray(temas)) { + temas = Array.of(temas); + } + return { preguntas, respuestas, temas }; } // Route for getting questions app.get('/questions', async (req, res) => { try { - // TODO: Implement logic to fetch questions from MongoDB and send response - // const questions = await Question.find() - const defaultQuestion = await getQuestions(req); - - try{ - const questionsHistoryResponse = await axios.post(questionHistoryServiceUrl + '/history/questions', defaultQuestion); + const { preguntas, respuestas, temas } = validateFields(req.query); + try { + const retQuestions = await QuestionGenerator.generateQuestions(preguntas, respuestas, temas); + try { + await axios.post(questionHistoryServiceUrl + '/history/questions', retQuestions); + } catch (error) { + console.error(`Error saving questions history: ${error}`); + } + res.json(retQuestions); } catch (error) { - console.error(`Error saving questions history: ${error}`); + console.error(`An error occurred: ${error.message}`); + res.status(500).json({ error: 'Internal Server Error' }); } - res.json(defaultQuestion); } catch (error) { - // res.status(500).json({ message: error.message }) - res.status(500).json({ error: 'Internal Server Error' }); + console.error(`Bad Request: ${error.message}`); + res.status(400).json({ message: error.message }); } }); diff --git a/questionsservice/questiongeneratorservice/questiongenerator.js b/questionsservice/questiongeneratorservice/questiongenerator.js new file mode 100644 index 00000000..89206f31 --- /dev/null +++ b/questionsservice/questiongeneratorservice/questiongenerator.js @@ -0,0 +1,101 @@ +const { Pais } = require('./questiongenerator-model') + +class QuestionGenerator { + + static temas = new Map([ + ["paises", [0, 1, 2]], + ['capital', [0]], + ["lenguaje", [1]] + ]); + ; + + static plantillas = [ + { + pregunta: (param) => `¿Cual es la capital de ${param}?`, + filtro: { capital: { $exists: true } }, + campo_pregunta: 'pais', + campo_respuesta: 'capital' + }, + { + pregunta: (param) => `¿Qué lengua se habla en ${param}?`, + filtro: { lenguaje: { $exists: true } }, + campo_pregunta: 'pais', + campo_respuesta: 'lenguaje' + } + // { + // pregunta: (param) => `¿Cuál es la bandera de ${param}?`, + // filtro: { bandera: { $exists: true } }, + // campo_pregunta: 'pais', + // campo_respuesta: 'bandera' + // } + ]; + + static async generateQuestion(plantilla, respuestas) { + console.log("\nPlantilla:"); + console.log(plantilla); + + const randomDocs = await Pais.aggregate([ + { $match: plantilla.filtro }, + { $sample: { size: respuestas } } + ]); + if (randomDocs.length < respuestas) { + console.error(`Not enought data found to generate a question`); + throw new Error(`Not enought data found to generate a question`); + } + + console.log("\nFind:"); + console.log(randomDocs); + + var retQuestion = { + pregunta: plantilla.pregunta(randomDocs[0][plantilla.campo_pregunta]), + respuesta_correcta: randomDocs[0][plantilla.campo_respuesta], + respuestas_incorrectas: Array.from({ length: respuestas-1 }, (_, i) => randomDocs[i+1][plantilla.campo_respuesta]) + }; + console.log("\nPregunta generada:"); + console.log(retQuestion); + + return retQuestion; + } + + static async generateQuestions(preguntas, respuestas, temas) { + console.log(temas); + const plantillasDisponibles = this.getAvailableTemplates(temas); + console.log(plantillasDisponibles); + var retQuestions = []; + for (let i = 0; i < preguntas; i++) { + let index = Math.floor(Math.random() * plantillasDisponibles.length); + retQuestions.push(await this.generateQuestion(this.plantillas[plantillasDisponibles[index]], respuestas)); + } + return retQuestions; + } + + static getAvailableTemplates(temas) { + if (temas.length == 0) { + return Array.from({ length: this.plantillas.length }, (_, i) => i); + } + var templates = []; + temas.forEach(tema => { + console.log(tema); + if (this.temas.has(tema)) { + templates = templates.concat(this.temas.get(tema)); + console.log(this.temas.get(tema)); + } + else { + console.error(`The topic \'${tema}\' is not currently defined`); + throw new Error(`The topic \'${tema}\' is not currently defined`); + } + }); + if (templates.length == 0) { + console.error(`No correct topics were passed`); + throw new Error(`No correct topics were passed`); + } + console.log(templates); + console.log([...new Set(templates)]); + return [...new Set(templates)]; + } + +} + +module.exports = { + QuestionGenerator +}; \ No newline at end of file diff --git a/questionsservice/wikidataExtractor/wikidataextractor-service.js b/questionsservice/wikidataExtractor/wikidataextractor-service.js index 3586e4d2..9d7084d1 100644 --- a/questionsservice/wikidataExtractor/wikidataextractor-service.js +++ b/questionsservice/wikidataExtractor/wikidataextractor-service.js @@ -55,7 +55,7 @@ cron.schedule(`*/${minutes} * * * *`, () => { // Route for extracting countries app.get('/extract', async (req, res) => { try { - res.json(extractData()); + res.json(await extractData()); } catch (error) { res.status(500).json({ message: error.message }) // res.status(500).json({ error: 'Internal Server Error' });