diff --git a/docker-compose.yml b/docker-compose.yml index 2caf2f81..04eca969 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,21 @@ services: networks: - mynetwork environment: - MONGODB_URI: mongodb://mongodb:27017/userdb + MONGODB_URI: mongodb://mongodb:27017/questiondb + + retrieveservice: + container_name: retrieveservice-${teamname:-defaultASW} + image: ghcr.io/arquisoft/wiq_es2b/retrieveservice:latest + profiles: ["dev", "prod"] + build: ./questions/retrieveservice + depends_on: + - mongodb + ports: + - "8004:8004" + networks: + - mynetwork + environment: + MONGODB_URI: mongodb://mongodb:27017/questiondb authservice: container_name: authservice-${teamname:-defaultASW} @@ -63,6 +77,7 @@ services: - userservice - authservice - creationservice + - retrieveservice ports: - "8000:8000" networks: @@ -71,6 +86,7 @@ services: AUTH_SERVICE_URL: http://authservice:8002 USER_SERVICE_URL: http://userservice:8001 CREATION_SERVICE_URL: http://creationservice:8005 + RETRIEVE_SERVICE_URL: http://retrieveservice:8004 webapp: container_name: webapp-${teamname:-defaultASW} diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 6d38f474..a20b94cc 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -9,6 +9,7 @@ const port = 8000; const authServiceUrl = process.env.AUTH_SERVICE_URL || 'http://localhost:8002'; const userServiceUrl = process.env.USER_SERVICE_URL || 'http://localhost:8001'; const creationServiceUrl = process.env.CREATION_SERVICE_URL || 'http://localhost:8005'; +const retrieveServiceUrl = process.env.RETRIEVE_SERVICE_URL || 'http://localhost:8004'; app.use(cors()); app.use(express.json()); @@ -43,10 +44,18 @@ app.post('/adduser', async (req, res) => { app.post('/createquestion', async (req, res) => { try { // Create a petition to the URL (le llegará a creation-service.js) with the option /createquestion and the req.body params - console.log("salgo de gateway hacia creation"); const questionResponse = await axios.post(creationServiceUrl+'/createquestion', req.body); - console.log("vengo de creation y estoy en gateway"); - console.log(questionResponse.status); + // Return a json response with what we obtained on the petition + res.json(questionResponse.data); + } catch (error) { + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + +app.post('/getquestionshistory', async (req, res) => { + try { + // Create a petition to the URL (le llegará a retrieve-service.js) with the option /getgeneratedquestions and the req.body params + const questionResponse = await axios.post(retrieveServiceUrl+'/getquestionshistory', req.body); // Return a json response with what we obtained on the petition res.json(questionResponse.data); } catch (error) { diff --git a/questions/creationservice/creation-model.js b/questions/creationservice/creation-model.js index a8a3723c..378bf26d 100644 --- a/questions/creationservice/creation-model.js +++ b/questions/creationservice/creation-model.js @@ -2,11 +2,26 @@ const mongoose = require('mongoose'); // Crea la base de datos con las columnas especificadas const questionSchema = new mongoose.Schema({ - question: String, - correctAnswer: String, - incorrectAnswer1: String, - incorrectAnswer2: String, - incorrectAnswer3: String, + question: { + type: String, + required: true, + }, + correctAnswer: { + type: String, + required: true, + }, + incorrectAnswer1: { + type: String, + required: true, + }, + incorrectAnswer2: { + type: String, + required: true, + }, + incorrectAnswer3: { + type: String, + required: true, + }, }); const Question = mongoose.model('Question', questionSchema); diff --git a/questions/creationservice/creation-service.js b/questions/creationservice/creation-service.js index 8e9ab4eb..7fc89323 100644 --- a/questions/creationservice/creation-service.js +++ b/questions/creationservice/creation-service.js @@ -1,17 +1,21 @@ const express = require('express'); const mongoose = require('mongoose'); const fetch = require('node-fetch'); +const Question = require('./creation-model'); const app = express(); const port = 8005; app.use(express.json()); +const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/questiondb'; +mongoose.connect(mongoUri); + const optionsNumber = 4; -// It will be the country of the question +// It will be the questionObject var questionObject= ""; -// It will be the correct capital of the question +// It will be the correct answer var correctOption = ""; // It will be the different options for the answers var answerOptions = []; @@ -22,7 +26,7 @@ var queries = ['SELECT DISTINCT ?questionObject ?questionObjectLabel ?answer ?an // Array of the possible questions var questions = ["¿Cual es la capital de "]; -// Recieves the information of the query and select wich data use on the question (country and capitals) +// Recieves the information of the query and select wich data use on the question function getQuestionInfo(info){ answerOptions = []; var fourRows = []; @@ -47,6 +51,28 @@ function selectRandomQuery(){ randomQuerySelector = Math.floor(Math.random() * queries.length); } +async function saveQuestion(){ + var incorrectAnswers=[]; + answerOptions.forEach(e => { + if(e!=correctOption) + incorrectAnswers.push(e); + }); + + try { + const newQuestion = new Question({ + question: questionObject, + correctAnswer: correctOption, + incorrectAnswer1: incorrectAnswers[0], + incorrectAnswer2: incorrectAnswers[1], + incorrectAnswer3: incorrectAnswers[2] + }); + await newQuestion.save(); + + }catch (error){ + console.error("Error al guardar la pregunta: " + error); + } +} + app.post('/createquestion', async (req, res) => { selectRandomQuery(); const apiUrl = `https://query.wikidata.org/sparql?query=${encodeURIComponent(queries[randomQuerySelector])}&format=json`; @@ -77,6 +103,8 @@ app.post('/createquestion', async (req, res) => { responseCorrectOption : correctOption, responseAnswerOptions : answerOptions }; + + saveQuestion(); // Return the resoult with a 200 status res.status(200).json(solution); diff --git a/questions/creationservice/package.json b/questions/creationservice/package.json index aeddd4c6..7c85d114 100644 --- a/questions/creationservice/package.json +++ b/questions/creationservice/package.json @@ -1,7 +1,7 @@ { "name": "creationservice", "version": "1.0.0", - "description": " Creation service, in charge of ", + "description": " Creation service, in charge of create the questions of the game and store them", "main": "service.js", "scripts": { "start": "node creation-service.js", diff --git a/questions/retrieveservice/.dockerignore b/questions/retrieveservice/.dockerignore new file mode 100644 index 00000000..3091757a --- /dev/null +++ b/questions/retrieveservice/.dockerignore @@ -0,0 +1,2 @@ +node_modules +coverage \ No newline at end of file diff --git a/questions/retrieveservice/Dockerfile b/questions/retrieveservice/Dockerfile new file mode 100644 index 00000000..838d0d31 --- /dev/null +++ b/questions/retrieveservice/Dockerfile @@ -0,0 +1,20 @@ +# Use an official Node.js runtime as a parent image +FROM node:20 + +# Set the working directory in the container +WORKDIR /usr/src/retrieveservice + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install app dependencies +RUN npm install + +# Copy the app source code to the working directory +COPY . . + +# Expose the port the app runs on +EXPOSE 8004 + +# Define the command to run your app +CMD ["node", "retrieve-service.js"] diff --git a/questions/retrieveservice/package.json b/questions/retrieveservice/package.json new file mode 100644 index 00000000..c2cf50e7 --- /dev/null +++ b/questions/retrieveservice/package.json @@ -0,0 +1,32 @@ +{ + "name": "retrieveservice", + "version": "1.0.0", + "description": " Retrieve service, in charge of claim the questions information stored on de database", + "main": "service.js", + "scripts": { + "start": "node retrieve-service.js", + "test": "jest" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/arquisoft/wiq_es2b.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/arquisoft/wiq_es2b/issues" + }, + "homepage": "https://github.com/arquisoft/wiq_es2b#readme", + "dependencies": { + "bcrypt": "^5.1.1", + "body-parser": "^1.20.2", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.0.4" + }, + "devDependencies": { + "jest": "^29.7.0", + "mongodb-memory-server": "^9.1.5", + "supertest": "^6.3.4" + } +} diff --git a/questions/retrieveservice/questionshistory-model.js b/questions/retrieveservice/questionshistory-model.js new file mode 100644 index 00000000..bc9e641e --- /dev/null +++ b/questions/retrieveservice/questionshistory-model.js @@ -0,0 +1,14 @@ +const mongoose = require('mongoose'); + +// Crea la base de datos con las columnas especificadas +const questionSchema = new mongoose.Schema({ + question: String, + correctAnswer: String, + incorrectAnswer1: String, + incorrectAnswer2: String, + incorrectAnswer3: String +}); + +const Question = mongoose.model('Question', questionSchema); + +module.exports = Question \ No newline at end of file diff --git a/questions/retrieveservice/retrieve-service.js b/questions/retrieveservice/retrieve-service.js new file mode 100644 index 00000000..3ece0bc3 --- /dev/null +++ b/questions/retrieveservice/retrieve-service.js @@ -0,0 +1,33 @@ +const express = require('express'); +const mongoose = require('mongoose'); +const Question = require('./questionshistory-model') + +const app = express(); +const port = 8004; + +app.use(express.json()); + +const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/questiondb'; +mongoose.connect(mongoUri); + + +app.post('/getquestionshistory', async (req, res) => { + const questions = await Question.find({}); + + var solution = []; + questions.forEach(row => { + solution.push([row.question,row.correctAnswer,row.incorrectAnswer1,row.incorrectAnswer2,row.incorrectAnswer3]); + }); + + res.status(200).json(solution); +}); + +const server = app.listen(port, () => { + console.log(`Creation Service listening at http://localhost:${port}`); +}); + +server.on('close', () => { + mongoose.connection.close(); +}); + +module.exports = server; diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index 0fa8bb48..005d7ade 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -1,7 +1,6 @@ - import React, { useState, useEffect } from 'react'; import axios from 'axios'; -import { Container, Typography, Button, Paper, TimerIcon } from '@mui/material'; +import { Container, Typography, Button, Paper} from '@mui/material'; import './Game.css'; @@ -26,7 +25,7 @@ const Game = () => { useEffect(() => { handleShowQuestion(); - }, []); + }); useEffect(() => { @@ -63,7 +62,6 @@ const Game = () => { // Method that checks if the answer clicked is the correct one const handleAnswerClick = (option, index) => { // Get what component is the button to change its color later - //const button = document.getElementById(`button_${index}`); if(option === correctOption) { const buttonId = `button_${index}`; const correctButton = document.getElementById(buttonId); @@ -109,15 +107,15 @@ const Game = () => { Saber y Ganar Juego - Pregunta {questionCounter}: ¿Cuál es la capital de {questionObject}? + Pregunta {questionCounter}: {questionObject}
- {answerOptions.map((option, index) => ( - - ))} -
+ {answerOptions.map((option, index) => ( + + ))} +
- + - -
- Time Remaining: {Math.floor(seconds / 60)}:{(seconds % 60).toLocaleString('en-US', { minimumIntegerDigits: 2 })} +
+ Time Remaining: {Math.floor(seconds / 60)}:{(seconds % 60).toLocaleString('en-US', { minimumIntegerDigits: 2 })} +
-
- - - {/* */} - ); diff --git a/webapp/src/components/HistoricalData.js b/webapp/src/components/HistoricalData.js index c96207ca..6e746026 100644 --- a/webapp/src/components/HistoricalData.js +++ b/webapp/src/components/HistoricalData.js @@ -1,13 +1,66 @@ -import React, { useState } from 'react'; import axios from 'axios'; -import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; +import React, { useState} from 'react'; +import { useNavigate} from 'react-router-dom'; +import { Container, Button} from '@mui/material'; const HistoricalData = () => { + const navigate = useNavigate(); + const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + + const [questionsHistory, setQuestionsHistory] = useState([]); + + const handleShowHistory = async () => { + try{ + // It makes a petition to the api and store the response + const response = await axios.post(`${apiEndpoint}/getquestionshistory`, { }); + setQuestionsHistory(response.data); + }catch (error){ + console.error('Error:', error); + } + } + + const handlePreviousPage = async () => { + let path= '/MainPage'; + navigate(path); + } return ( - - Pagina del HistoricalData - + + + +
+ + + +
+
+ + + + + + + + + + + + {questionsHistory.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + + ))} + + ))} + +
PreguntaOpción correctaOpción incorrecta 1Opción incorrecta 2Opción incorrecta 3
{cell}
+
+
+ ); }; diff --git a/webapp/src/components/MainPage.js b/webapp/src/components/MainPage.js index a862e675..17f1dffe 100644 --- a/webapp/src/components/MainPage.js +++ b/webapp/src/components/MainPage.js @@ -1,6 +1,5 @@ -import React, { useState } from 'react'; -import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; -import { useNavigate, useParams } from 'react-router-dom'; +import { Container, Typography, Button} from '@mui/material'; +import { useNavigate} from 'react-router-dom'; const MainPage = () => { const navigate = useNavigate();