diff --git a/gamehistoryservice/gamehistory.js b/gamehistoryservice/gamehistory.js index 2c1a069a..ea9f939b 100644 --- a/gamehistoryservice/gamehistory.js +++ b/gamehistoryservice/gamehistory.js @@ -43,8 +43,8 @@ app.get("/gamehistory", async (req, res) => { totalQuestionsAnswered: gamehistory.totalQuestionsAnswered, totalRightQuestions: gamehistory.totalRightQuestions, totalIncorrectQuestions: gamehistory.totalIncorrectQuestions, - ratio: gamehistory.ratio + " %", - totalTime: gamehistory.totalTime + " s" + ratio: gamehistory.ratio + "%", + totalTime: gamehistory.totalTime + "s" }; res.json(response); } else { @@ -65,6 +65,28 @@ app.get("/gamehistory", async (req, res) => { } }); +app.get("/endgamestats", async (req, res) => { + try { + const userId = req.query.username; + const gameStats = await getEndGameStats(userId); + + // Formatea la respuesta JSON + const response = { + totalRightQuestions: gameStats.totalRightQuestions || 0, + totalIncorrectQuestions: gameStats.totalIncorrectQuestions || 0, + ratio: (gameStats.ratio || 0) + "%", + totalTime: (gameStats.totalTime || 0) + "s" + }; + + // Envía la respuesta JSON + res.json(response); + + } catch (error) { + res.status(400).json({ error: "Error al obtener las estadísticas de la partida: " + error.message }); + } +}); + + async function saveGameHistory(userId) { try { @@ -126,9 +148,9 @@ app.get("/topUsers", async (req, res) => { const topUsers = await GameHistory.find() .sort({ ratio: -1 }) // Ordena porcentaje de aciertos de mayor a menor const response = { - primero: topUsers[0] || null, - segundo: topUsers[1] || null, - tercero: topUsers[2] || null + primero: topUsers[0] ? topUsers[0].userId + " - " + topUsers[0].ratio + "%" : "", + segundo: topUsers[1] ? topUsers[1].userId + " - " + topUsers[1].ratio + "%": "", + tercero: topUsers[2] ? topUsers[2].userId + " - " + topUsers[2].ratio + "%": "" }; res.json(response); } catch (error) { @@ -136,6 +158,49 @@ app.get("/topUsers", async (req, res) => { } }); +async function getEndGameStats(userId) { + try { + // Busca las partidas del usuario y ordena por fecha descendente para obtener la última partida + const games = await mongoose.connection.collection('games').aggregate([ + { + $match: { userId: userId } + }, + { + $sort: { createdAt: -1 } + }, + { + $lookup: { + from: 'questions', + localField: 'questions', + foreignField: '_id', + as: 'questionsData' + } + }, + ]).toArray(); + + // Calcula las estadísticas de la última partida + const lastGame = games[0]; + const totalQuestionsAnswered = lastGame.questionsData.length; + const totalRightQuestions = lastGame.questionsData.filter(question => question.correct).length; + const totalIncorrectQuestions = totalQuestionsAnswered - totalRightQuestions; + const totalTime = lastGame.questionsData.reduce((acc, curr) => acc + (curr.time ?? 0), 0); + const ratio = totalQuestionsAnswered === 0 ? 0 : parseInt((totalRightQuestions / totalQuestionsAnswered) * 100); + + // Devuelve las estadísticas + return { + totalRightQuestions, + totalIncorrectQuestions, + ratio, + totalTime + }; + + } catch (error) { + console.error('Error al obtener las estadísticas de la partida:', error); + throw error; + } +} + + const server = app.listen(port, () => { console.log(`Stats Service listening at http://localhost:${port}`); }); diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 8da47664..6822741f 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -96,6 +96,16 @@ app.get('/topUsers', async (req, res) => { } }); +app.get('/endgamestats', async (req, res) => { + try { + const URL = gamehistoryUrl + '/endgamestats?username=' + req.query.username; + const response = await axios.get(URL); + res.json(response.data); + } catch (error) { + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + // Read the OpenAPI YAML file synchronously // Hubo que cambiar el path porque los test e2e ahora sólo se ejecutan desde webapp openapiPath='../gatewayservice/openapi.yaml' diff --git a/questiongenerator/game-model.js b/questiongenerator/game-model.js index 8e97397d..9c30a4b6 100644 --- a/questiongenerator/game-model.js +++ b/questiongenerator/game-model.js @@ -1,7 +1,7 @@ const mongoose = require('mongoose'); const gameSchema = new mongoose.Schema({ - userId:{ + userId: { type: String, ref: 'User', required: true, @@ -9,12 +9,15 @@ const gameSchema = new mongoose.Schema({ questions: [ { type: mongoose.Schema.Types.ObjectId, - ref:'Question' + ref: 'Question' } - ] - + ], + createdAt: { + type: Date, + default: Date.now + } }); const Game = mongoose.model('Game', gameSchema); -module.exports = Game \ No newline at end of file +module.exports = Game; \ No newline at end of file diff --git a/webapp/src/components/EndGame.js b/webapp/src/components/EndGame.js new file mode 100644 index 00000000..959b7625 --- /dev/null +++ b/webapp/src/components/EndGame.js @@ -0,0 +1,86 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import axios from 'axios'; +import { Container, Box, Typography, Grid, Button} from '@mui/material'; +import { useUser } from './UserContext'; +import { useNavigate } from 'react-router-dom'; +import HomeIcon from '@mui/icons-material/Home'; +import '../App.css'; + +const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + +const EndGame = () => { + + const { usernameGlobal } = useUser(); + const navigate = useNavigate(); + const [endGame, setEndGame] = useState(''); + const [error, setError] = useState(''); + + const getEndGame = useCallback(async () => { + try { + const response = await axios.get(`${apiEndpoint}/endgamestats`,{ + params: { + username: usernameGlobal + } + }); + setEndGame(response.data); + } catch (error) { + setError(error.response.data.error); + } + }, [usernameGlobal]) + + function volverInicio() { + navigate("/PantallaInicio"); + } + + useEffect(() => { + getEndGame(); + }, [getEndGame]); + + return ( + + + End Game + + Estadísticas de la última partida + + + + + Preguntas correctas: {endGame.totalRightQuestions} + + + + + Preguntas incorrectas: {endGame.totalIncorrectQuestions} + + + + + Ratio de aciertos: {endGame.ratio} + + + + + Tiempo total: {endGame.totalTime} + + + + + + + + + ); +}; + +export default EndGame; \ No newline at end of file diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index b4f76bd9..148c7cbe 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -72,9 +72,11 @@ const Game = () => { if(elapsedTime<=0){ setIsTimeRunning(false); if (answeredQuestions >= MAX_PREGUNTAS) { - setAnsweredQuestions(0); - saveGameHistory(); - navigate("/PantallaInicio"); + setTimeout(() => { + setAnsweredQuestions(0); + saveGameHistory(); + navigate("/EndGame"); + }, 3000); }else{ getQuestion(); } @@ -114,7 +116,7 @@ const Game = () => { setTimeout(() => { setAnsweredQuestions(0); saveGameHistory(); - navigate("/PantallaInicio"); + navigate("/EndGame"); }, 3000); }else{ setTimeout(() => { diff --git a/webapp/src/components/PantallaInicio.js b/webapp/src/components/PantallaInicio.js index c3c2ad3f..522c9f67 100644 --- a/webapp/src/components/PantallaInicio.js +++ b/webapp/src/components/PantallaInicio.js @@ -56,6 +56,7 @@ const PantallaInicio = () => { )} + ); }; diff --git a/webapp/src/components/Ranking.js b/webapp/src/components/Ranking.js index 86430bc3..b190f0cf 100644 --- a/webapp/src/components/Ranking.js +++ b/webapp/src/components/Ranking.js @@ -1,7 +1,6 @@ import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import { Container, Box, Typography, Grid} from '@mui/material'; -import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule'; import { useUser } from './UserContext'; import '../App.css'; @@ -28,48 +27,48 @@ const Ranking = () => { return ( - - {/* Fila con una columna para el primero */} - - {ranking && ( - - Primero - - {ranking.primero.userId} - {ranking.primero.ratio}% - - )} - - {/* Fila con dos columnas para el segundo y tercero */} - - + sx={{ + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + height: '100vh', + width: '100%', + }}> + + + Top 3 Usuarios + + + {ranking && ( - Segundo - - {ranking.segundo.userId} - {ranking.segundo.ratio}% + Primero + {ranking.primero} )} - - {ranking && ( - - Tercero - - {ranking.tercero.userId} - {ranking.tercero.ratio}% - - )} + + + {ranking && ( + + Segundo + {ranking.segundo} + + )} + + + {ranking && ( + + Tercero + {ranking.tercero} + + )} + - - + + ); }; diff --git a/webapp/src/components/fragments/Footer.js b/webapp/src/components/fragments/Footer.js new file mode 100644 index 00000000..e216ee06 --- /dev/null +++ b/webapp/src/components/fragments/Footer.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; + +const Footer = () => { + return ( + + © {new Date().getFullYear()} ASW - WIQ_ES5B + + ); +} + +export default Footer; diff --git a/webapp/src/components/NavigationBar.js b/webapp/src/components/fragments/NavigationBar.js similarity index 77% rename from webapp/src/components/NavigationBar.js rename to webapp/src/components/fragments/NavigationBar.js index 6b66ed4e..5fd3e8dd 100644 --- a/webapp/src/components/NavigationBar.js +++ b/webapp/src/components/fragments/NavigationBar.js @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import { AppBar, Button, Grid } from '@mui/material'; -import { useUser } from './UserContext'; +import { useUser } from '../UserContext'; const NavigationBar = () => { @@ -47,22 +47,22 @@ const NavigationBar = () => { {/* Columna izquierda */} {/* Columna derecha */} diff --git a/webapp/src/components/NavigationBar.test.js b/webapp/src/components/fragments/NavigationBar.test.js similarity index 89% rename from webapp/src/components/NavigationBar.test.js rename to webapp/src/components/fragments/NavigationBar.test.js index 6628def5..3145e822 100644 --- a/webapp/src/components/NavigationBar.test.js +++ b/webapp/src/components/fragments/NavigationBar.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { render} from '@testing-library/react'; import { MemoryRouter as Router } from "react-router-dom"; -import { UserProvider } from './UserContext'; +import { UserProvider } from '../UserContext'; import NavigationBar from './NavigationBar'; describe('Navigation bar component', () => { diff --git a/webapp/src/components/images/perroBasado.png b/webapp/src/components/images/perroBasado.png new file mode 100644 index 00000000..f4caba53 Binary files /dev/null and b/webapp/src/components/images/perroBasado.png differ diff --git a/webapp/src/components/images/ronnie.gif b/webapp/src/components/images/ronnie.gif new file mode 100644 index 00000000..18a7b2ea Binary files /dev/null and b/webapp/src/components/images/ronnie.gif differ diff --git a/webapp/src/index.js b/webapp/src/index.js index 49bb1d72..bd086078 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; -import NavigationBar from './components/NavigationBar'; import reportWebVitals from './reportWebVitals'; import { UserProvider } from './components/UserContext'; @@ -18,6 +17,9 @@ import AddUser from './components/AddUser'; import Game from './components/Game'; import Gamehistory from './components/Gamehistory'; import Ranking from './components/Ranking'; +import EndGame from './components/EndGame'; +import Footer from './components/fragments/Footer'; +import NavigationBar from './components/fragments/NavigationBar'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( @@ -34,7 +36,9 @@ root.render( } /> } /> } /> + } /> +