diff --git a/docker-compose.yml b/docker-compose.yml
index a968fee3..3da76ec3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -28,14 +28,12 @@ services:
build: ./statsservice
depends_on:
- mongodb
- - userservice
ports:
- "8004:8004"
networks:
- mynetwork
environment:
- MONGODB_URI: mongodb://mongodb:27017/userdb
- USER_SERVICE_URL: http://userservice:8001
+ MONGODB_URI: mongodb://mongodb:27017/statsdb
authservice:
container_name: authservice-${teamname:-defaultASW}
diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js
index 0fcc3d1c..427bd2f1 100644
--- a/gatewayservice/gateway-service.js
+++ b/gatewayservice/gateway-service.js
@@ -10,7 +10,7 @@ const authServiceUrl = process.env.AUTH_SERVICE_URL || "http://localhost:8002";
const userServiceUrl = process.env.USER_SERVICE_URL || "http://localhost:8001";
const questionServiceUrl =
process.env.QUESTION_SERVICE_URL || "http://localhost:8003";
-const statsServiceUrl = process.env.AUTH_SERVICE_URL || "http://localhost:8004";
+const statsServiceUrl = process.env.STATS_SERVICE_URL || "http://localhost:8004";
app.use(cors());
app.use(express.json());
@@ -112,10 +112,9 @@ app.post("/saveGame", async (req, res) => {
}
});
-app.get("/getstats", async (req, res) => {
+app.get("/ranking", async (req, res) => {
try {
- // Forward the stats request to the stats service
- const statsResponse = await axios.get(userServiceUrl + "/getstats", {
+ const statsResponse = await axios.get(statsServiceUrl + "/ranking", {
params: req.query,
});
res.json(statsResponse.data);
@@ -126,21 +125,6 @@ app.get("/getstats", async (req, res) => {
}
});
-app.post("/userSaveGame", async (req, res) => {
- try {
- // Forward the save game request to the stats service
- const gameResponse = await axios.post(
- userServiceUrl + "/userSaveGame",
- req.body
- );
- res.json(gameResponse.data);
- } catch (error) {
- res
- .status(error.response.status)
- .json({ error: error.response.data.error });
- }
-});
-
// Start the gateway service
const server = app.listen(port, () => {
console.log(`Gateway Service listening at http://localhost:${port}`);
diff --git a/package-lock.json b/package-lock.json
index 477baab9..fbee59f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "wiq_es1a",
- "lockfileVersion": 3,
+ "lockfileVersion": 2,
"requires": true,
"packages": {
"": {
@@ -18078,5 +18078,389 @@
"url": "https://github.com/sponsors/sindresorhus"
}
}
+ },
+ "dependencies": {
+ "@next/env": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
+ "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==",
+ "peer": true
+ },
+ "@next/swc-darwin-arm64": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
+ "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-darwin-x64": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
+ "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-linux-arm64-gnu": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
+ "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-linux-arm64-musl": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
+ "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-linux-x64-gnu": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz",
+ "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-linux-x64-musl": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz",
+ "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-win32-arm64-msvc": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
+ "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-win32-ia32-msvc": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
+ "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
+ "optional": true,
+ "peer": true
+ },
+ "@next/swc-win32-x64-msvc": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
+ "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
+ "optional": true,
+ "peer": true
+ },
+ "@swc/helpers": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
+ "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
+ "peer": true,
+ "requires": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "@types/luxon": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.8.tgz",
+ "integrity": "sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ=="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "axios": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+ "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+ "requires": {
+ "follow-redirects": "^1.15.4",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "peer": true,
+ "requires": {
+ "streamsearch": "^1.1.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001583",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz",
+ "integrity": "sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==",
+ "peer": true
+ },
+ "client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "peer": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "cron": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.6.tgz",
+ "integrity": "sha512-cvFiQCeVzsA+QPM6fhjBtlKGij7tLLISnTSvFxVdnFGLdz+ZdXN37kNe0i2gefmdD17XuZA6n2uPVwzl4FxW/w==",
+ "requires": {
+ "@types/luxon": "~3.3.0",
+ "luxon": "~3.4.0"
+ }
+ },
+ "cross-fetch": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
+ "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
+ "requires": {
+ "node-fetch": "^2.6.12"
+ }
+ },
+ "date-fns": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz",
+ "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw=="
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "follow-redirects": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
+ "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw=="
+ },
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "peer": true
+ },
+ "js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "peer": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "peer": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "luxon": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
+ "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA=="
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "peer": true
+ },
+ "next": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
+ "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==",
+ "peer": true,
+ "requires": {
+ "@next/env": "14.1.0",
+ "@next/swc-darwin-arm64": "14.1.0",
+ "@next/swc-darwin-x64": "14.1.0",
+ "@next/swc-linux-arm64-gnu": "14.1.0",
+ "@next/swc-linux-arm64-musl": "14.1.0",
+ "@next/swc-linux-x64-gnu": "14.1.0",
+ "@next/swc-linux-x64-musl": "14.1.0",
+ "@next/swc-win32-arm64-msvc": "14.1.0",
+ "@next/swc-win32-ia32-msvc": "14.1.0",
+ "@next/swc-win32-x64-msvc": "14.1.0",
+ "@swc/helpers": "0.5.2",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "graceful-fs": "^4.2.11",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.1"
+ }
+ },
+ "node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "peer": true
+ },
+ "postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "peer": true,
+ "requires": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "peer": true,
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "react-auth-kit": {
+ "version": "3.0.2-alpha.19",
+ "resolved": "https://registry.npmjs.org/react-auth-kit/-/react-auth-kit-3.0.2-alpha.19.tgz",
+ "integrity": "sha512-xZStv05wItit/AN2qgSErYCtpn9u8rr3cpgMxG8ZOkJMcYa4wvFPkYoAWT5rkOuP85fp9B0AqGb5tUen0B5yeg==",
+ "requires": {
+ "js-cookie": "^3.0.1",
+ "rxjs": "^7.8.1"
+ }
+ },
+ "react-dom": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+ "peer": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ }
+ },
+ "rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "scheduler": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+ "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "peer": true,
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "peer": true
+ },
+ "streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "peer": true
+ },
+ "styled-jsx": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
+ "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
+ "peer": true,
+ "requires": {
+ "client-only": "0.0.1"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
}
}
diff --git a/statsservice/model/partidas.json b/statsservice/model/partidas.json
deleted file mode 100644
index 63dc9b4f..00000000
--- a/statsservice/model/partidas.json
+++ /dev/null
@@ -1,38 +0,0 @@
-[
- {
- "username": "vagrant",
- "points": 200,
- "correctQuestions": 7,
- "incorrectQuestions": 3
- },
- {
- "username": "vagrant",
- "points": 400,
- "correctQuestions": 16,
- "incorrectQuestions": 4
- },
- {
- "username": "jose123",
- "points": 200,
- "correctQuestions": 8,
- "incorrectQuestions": 2
- },
- {
- "username": "vagrant",
- "points": 1000,
- "correctQuestions": 40,
- "incorrectQuestions": 10
- },
- {
- "username": "rober200",
- "points": 100,
- "correctQuestions": 3,
- "incorrectQuestions": 2
- },
- {
- "username": "jose123",
- "points": 40,
- "correctQuestions": 1,
- "incorrectQuestions": 1
- }
-]
\ No newline at end of file
diff --git a/statsservice/model/stats-getter.js b/statsservice/model/stats-getter.js
index 86d09305..cf47411d 100644
--- a/statsservice/model/stats-getter.js
+++ b/statsservice/model/stats-getter.js
@@ -1,50 +1,83 @@
-const User = require('../../users/userservice/user-model.js');
+const Stats = require('./stats-model.js');
class StatsForUser {
- async getStatsForUser(username){
- try {
- const user = await User.findOne({ username: username });
-
- if (!user) {
- throw new Error('Usuario no encontrado');
+ async getStatsForUser(username,gamemode){
+ var statsJSON=null;
+ if(gamemode=="clasico" || gamemode=="bateria"){
+ statsJSON = await this.getStats(username,gamemode);
}
+ return statsJSON;
+
+ }
- const partidas = user.games;
-
- var nGamesPlayed = partidas.length;
- var totalPoints = 0;
- var totalCorrectQuestions = 0;
- var totalIncorrectQuestions = 0;
-
- for (const partida of partidas){
- totalPoints += partida.points;
- totalCorrectQuestions += partida.correctAnswers;
- totalIncorrectQuestions += partida.incorrectAnswers;
+ async getStats(username,gamemode){
+ try {
+ var stats = await Stats.findOne({ username:username,gamemode:gamemode });
+
+ if (stats) {
+ return {
+ username: stats.username,
+ gamemode: stats.gamemode,
+ nGamesPlayed: stats.nGamesPlayed,
+ avgPoints: stats.avgPoints,
+ totalPoints: stats.totalPoints,
+ totalCorrectQuestions: stats.totalCorrectQuestions,
+ totalIncorrectQuestions: stats.totalIncorrectQuestions,
+ ratioCorrect: stats.ratioCorrect,
+ avgTime: stats.avgTime
+ };
+ } else {
+ return null; // Si no se encuentran estadísticas para el usuario
}
+ } catch (error) {
+ console.error('Error al obtener estadísticas:', error);
+ throw error;
+ }
+ }
- const avgPoints = nGamesPlayed > 0 ?
- totalPoints / nGamesPlayed : 0;
+ async calculateStats(username,gamemode,gameData,stats){
- const ratioCorrectToIncorrect = totalIncorrectQuestions !== 0 ?
- totalCorrectQuestions / totalIncorrectQuestions : totalCorrectQuestions;
+
+ const totalGamesPlayed = stats.nGamesPlayed + 1;
+ const newAvgPoints = (stats.avgPoints * stats.nGamesPlayed + gameData.points) / totalGamesPlayed;
+ const newTotalPoints = stats.totalPoints + gameData.points;
+ const newTotalCorrectQuestions = stats.totalCorrectQuestions + gameData.correctAnswers;
+ const newTotalIncorrectQuestions = stats.totalIncorrectQuestions + gameData.incorrectAnswers;
+ const newRatioCorrect = (newTotalCorrectQuestions / (newTotalIncorrectQuestions+newTotalCorrectQuestions))*100;
+ const newAvgTime = (stats.avgTime * stats.nGamesPlayed + gameData.avgTime) / totalGamesPlayed;
- const statsJSON = {
- username: username,
- nGamesPlayed: nGamesPlayed,
- avgPoints: avgPoints,
- totalPoints: totalPoints,
- totalCorrectQuestions: totalCorrectQuestions,
- totalIncorrectQuestions: totalIncorrectQuestions,
- ratioCorrectToIncorrect: ratioCorrectToIncorrect
- };
+ return {
+ username:username,
+ gamemode: gamemode,
+ nGamesPlayed: totalGamesPlayed,
+ avgPoints: newAvgPoints,
+ totalPoints: newTotalPoints,
+ totalCorrectQuestions: newTotalCorrectQuestions,
+ totalIncorrectQuestions: newTotalIncorrectQuestions,
+ ratioCorrect: newRatioCorrect,
+ avgTime: newAvgTime
+ };
+ }
- return statsJSON;
+ async getRanking(gamemode) {
+ try {
+ var stats = await Stats.find({gamemode:gamemode}).sort({ avgPoints: -1 }).limit(10);
+
+ if (stats && stats.length > 0) {
+ return stats.map(stat => ({
+ username: stat.username,
+ avgPoints: stat.avgPoints
+ }));
+ } else {
+ return null;
+ }
} catch (error) {
- console.error('Error al obtener las estadísticas del usuario:', error);
+ console.error('Error al obtener estadísticas:', error);
throw error;
}
}
+
}
module.exports = StatsForUser;
diff --git a/statsservice/model/stats-model.js b/statsservice/model/stats-model.js
new file mode 100644
index 00000000..a05a66a9
--- /dev/null
+++ b/statsservice/model/stats-model.js
@@ -0,0 +1,44 @@
+const mongoose = require('mongoose');
+
+const stats = new mongoose.Schema({
+ username: {
+ type: String,
+ required: true,
+ },
+ gamemode: {
+ type: String,
+ required: true,
+ },
+ nGamesPlayed: {
+ type: Number,
+ required: true,
+ },
+ avgPoints: {
+ type: Number,
+ required: true,
+ },
+ totalPoints: {
+ type: Number,
+ required: true,
+ },
+ totalCorrectQuestions: {
+ type: Number,
+ required: true,
+ },
+ totalIncorrectQuestions: {
+ type: Number,
+ required: true,
+ },
+ ratioCorrect: {
+ type: Number,
+ required: true,
+ },
+ avgTime: {
+ type: Number,
+ required: true,
+ },
+});
+
+const Stats = mongoose.model('Stats', stats);
+
+module.exports = Stats
diff --git a/statsservice/package-lock.json b/statsservice/package-lock.json
index 133a00de..d9bd0982 100644
--- a/statsservice/package-lock.json
+++ b/statsservice/package-lock.json
@@ -9,10 +9,8 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
-
"axios": "^1.6.7",
"cors": "^2.8.5",
-
"express": "^4.18.2",
"mongoose": "^8.2.0"
}
@@ -55,7 +53,6 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
-
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -71,7 +68,6 @@
"proxy-from-env": "^1.1.0"
}
},
-
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
@@ -99,7 +95,6 @@
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.4.0.tgz",
"integrity": "sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==",
-
"engines": {
"node": ">=16.20.1"
}
@@ -710,7 +705,6 @@
"node": ">=0.10.0"
}
},
-
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
diff --git a/statsservice/stats-service.js b/statsservice/stats-service.js
index b6f8fdb0..88ee82be 100644
--- a/statsservice/stats-service.js
+++ b/statsservice/stats-service.js
@@ -3,52 +3,97 @@ const express = require("express");
const bodyParser = require("body-parser");
const cors = require('cors');
const axios = require('axios');
+const Stats = require("./model/stats-model.js");
+const StatsForUser = require("./model/stats-getter.js");
+const mongoose = require('mongoose');
+
+const statsGetter= new StatsForUser();
const app = express();
const port = 8004;
-const userServiceUrl = process.env.USER_SERVICE_URL || "http://localhost:8001";
+const URL = process.env.GATEWAY_SERVICE_URL || "http://localhost:8000";
app.use(bodyParser.json());
app.use(cors());
app.set("json spaces", 40);
+const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/statsdb';
+mongoose.connect(mongoUri);
+
app.post("/saveGame", async (req, res) => {
try {
- const { username, correctAnswers, incorrectAnswers, points, avgTime } = req.body;
+ const username = req.body.username;
+ const gamemode = req.body.gameMode;
+ const gameData = req.body.gameData;
- // Hacer una solicitud al servicio user-service para guardar el juego
- const response = await axios.post(userServiceUrl + '/userSaveGame', {
- username,
- correctAnswers,
- incorrectAnswers,
- points,
- avgTime
- });
+ if (gamemode == "clasico" || gamemode == "bateria") {
+ // Buscar las estadísticas existentes del usuario y modo de juego
+ let stats = await Stats.findOne({ username: username, gamemode: gamemode });
- res.json(response.data);
+ if (!stats) {
+ // Si no existen estadísticas, crear un nuevo documento
+ stats = new Stats({
+ username: username,
+ gamemode: gamemode,
+ nGamesPlayed: 1,
+ avgPoints: gameData.points,
+ totalPoints: gameData.points,
+ totalCorrectQuestions: gameData.correctAnswers,
+ totalIncorrectQuestions: gameData.incorrectAnswers,
+ ratioCorrect: (gameData.correctAnswers / (gameData.incorrectAnswers + gameData.correctAnswers)) * 100,
+ avgTime: gameData.avgTime,
+ });
- user.games.push(newGame);
+ await stats.save();
+ } else {
- await user.save();
+ await Stats.updateOne(
+ { username: username, gamemode: gamemode },
+ {
+ $inc: {
+ nGamesPlayed: 1,
+ totalPoints: gameData.points,
+ totalCorrectQuestions: gameData.correctAnswers,
+ totalIncorrectQuestions: gameData.incorrectAnswers,
+ },
+ $set: {
+ avgPoints: (stats.avgPoints * stats.nGamesPlayed + gameData.points) / (stats.nGamesPlayed + 1),
+ ratioCorrect: ((stats.totalCorrectQuestions + gameData.correctAnswers) / (stats.totalIncorrectQuestions + stats.totalCorrectQuestions + gameData.incorrectAnswers + gameData.correctAnswers)) * 100,
+ avgTime: (stats.avgTime * stats.nGamesPlayed + gameData.avgTime) / (stats.nGamesPlayed + 1),
+ }
+ }
+ );
+ }
- res.json({ message: "Juego guardado exitosamente" });
+ res.json({ message: "Partida guardada exitosamente" });
+ }
} catch (error) {
- res.status(400).json({ error: error.message });
+ res.status(400).json({ error: "Error al guardar juego" + error.message });
}
});
-// Ruta para obtener estadísticas
+
app.get("/stats", async (req, res) => {
try {
- const username = req.query.user;
+ var data = await statsGetter.getStatsForUser(req.query.user,req.query.gamemode);
- // Hacer una solicitud al servicio user-service para obtener estadísticas
- const response = await axios.get(userServiceUrl + '/getstats?user=${username}');
+ res.json(data);
- res.json(response.data);
} catch (error) {
- res.status(400).json({ error: `Error al obtener las estadísticas: ${error.message}`});
+
+ res.status(400).json({ error: "Error al obtener las estadísticas:"+error.message });
+ }
+});
+
+app.get("/ranking", async (req, res) => {
+ try {
+ var data = await statsGetter.getRanking(req.query.gamemode);
+ res.json(data);
+
+ } catch (error) {
+
+ res.status(400).json({ error: "Error al obtener el ranking: "+error.message });
}
});
@@ -57,5 +102,10 @@ app.get("/stats", async (req, res) => {
const server = app.listen(port, () => {
console.log(`Stats Service listening at http://localhost:${port}`);
});
+
+server.on('close', () => {
+ // Close the Mongoose connection
+ mongoose.connection.close();
+});
module.exports = server;
\ No newline at end of file
diff --git a/statsservice/user-model.js b/statsservice/user-model.js
deleted file mode 100644
index 3add662e..00000000
--- a/statsservice/user-model.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const mongoose = require('mongoose');
-
-const userSchema = new mongoose.Schema({
- username: {
- type: String,
- required: true,
- },
- password: {
- type: String,
- required: true,
- },
- createdAt: {
- type: Date,
- default: Date.now,
- },
- games: [{
- correctAnswers: Number,
- incorrectAnswers: Number,
- points: Number,
- avgTime: Number
- }],
-});
-
-const User = mongoose.model('User', userSchema);
-
-module.exports = User
\ No newline at end of file
diff --git a/users/userservice/game-model.js b/users/userservice/game-model.js
deleted file mode 100644
index 04a2aba2..00000000
--- a/users/userservice/game-model.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const mongoose = require('mongoose');
-
-const gameSchema = new mongoose.Schema({
- // Atributos de la partida
- correctAnswers: {
- type: Number,
- default: 0
- },
- incorrectAnswers: {
- type: Number,
- default: 0
- },
- points: {
- type: Number,
- default: 0
- },
- avgTime: {
- type: Number,
- default: 0
- }
-});
-
-const Game = mongoose.model('Game', gameSchema);
-
-module.exports = Game;
diff --git a/users/userservice/stats-getter.js b/users/userservice/stats-getter.js
deleted file mode 100644
index 1a258504..00000000
--- a/users/userservice/stats-getter.js
+++ /dev/null
@@ -1,57 +0,0 @@
-const User = require('./user-model.js');
-
-
-class StatsForUser {
-
- async getStatsForUser(username){
- try {
- const user = await User.findOne({ username: username });
-
- if (!user) {
- throw new Error('Usuario no encontrado');
- }
-
- const partidas = user.games;
-
- var nGamesPlayed = partidas.length;
- var totalPoints = 0;
- var totalCorrectQuestions = 0;
- var totalIncorrectQuestions = 0;
- var totalTime=0;
-
- for (const partida of partidas){
- totalPoints += partida.points;
- totalCorrectQuestions += partida.correctAnswers;
- totalIncorrectQuestions += partida.incorrectAnswers;
- totalTime+=partida.avgTime;
- }
-
- var avgPoints = nGamesPlayed > 0 ?
- totalPoints / nGamesPlayed : 0;
-
- var ratioCorrectToIncorrect = totalIncorrectQuestions !== 0 ?
- totalCorrectQuestions / totalIncorrectQuestions : totalCorrectQuestions;
-
- var avgTime = nGamesPlayed > 0 ? totalTime / nGamesPlayed : 0;
-
- var statsJSON = {
- username: username,
- nGamesPlayed: nGamesPlayed,
- avgPoints: avgPoints,
- totalPoints: totalPoints,
- totalCorrectQuestions: totalCorrectQuestions,
- totalIncorrectQuestions: totalIncorrectQuestions,
- ratioCorrectToIncorrect: ratioCorrectToIncorrect,
- avgTime: avgTime
- };
-
- return statsJSON;
- } catch (error) {
- console.error('Error al obtener las estadísticas del usuario:', error);
- throw error;
- }
- }
-}
-
-module.exports = StatsForUser;
-
diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js
index f735df91..17ac1134 100644
--- a/users/userservice/user-service.js
+++ b/users/userservice/user-service.js
@@ -4,9 +4,6 @@ const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const bodyParser = require('body-parser');
const User = require('./user-model');
-const StatsForUser = require("./stats-getter.js");
-
-const statsGetter= new StatsForUser();
const app = express();
const port = 8001;
@@ -49,49 +46,6 @@ app.post('/adduser', async (req, res) => {
res.status(400).json({ error: error.message });
}});
- app.post("/userSaveGame", async (req, res) => {
- try {
- const { username, correctAnswers, incorrectAnswers, points, avgTime } = req.body;
-
- // Encontrar al usuario en la base de datos
- const user = await User.findOne({ username });
-
- if (!user) {
- return res.status(404).json({ error: "Usuario no encontrado" });
- }
-
- // Crear un nuevo juego
- const newGame = {
- correctAnswers,
- incorrectAnswers,
- points,
- avgTime
- };
-
- user.games.push(newGame);
-
- await user.save();
-
- res.json({ message: "Partida guardada exitosamente" });
- } catch (error) {
- res.status(400).json({ error: "Error al guardar partida"});
- }
- });
-
- app.get("/getstats", async (req, res) => {
- const user = await User.findOne({ username:req.query.user });
-
- if (!user) {
- return res.status(404).json({ error: "Usuario no encontrado" });
- }
- try {
- var data = await statsGetter.getStatsForUser(req.query.user);
- res.json(data);
- } catch (error) {
- res.status(400).json({ error: "Error al obtener las estadísticas" });
- }
- });
-
const server = app.listen(port, () => {
console.log(`User Service listening at http://localhost:${port}`);
});
diff --git a/webapp/src/App.css b/webapp/src/App.css
index 1b3fda63..328ee684 100644
--- a/webapp/src/App.css
+++ b/webapp/src/App.css
@@ -1,3 +1,4 @@
+
#root {
height: 100vh;
display: flex;
diff --git a/webapp/src/components/Login/Login.js b/webapp/src/components/Login/Login.js
index 1f89d808..54b5acec 100644
--- a/webapp/src/components/Login/Login.js
+++ b/webapp/src/components/Login/Login.js
@@ -1,8 +1,8 @@
// src/components/Login.js
-import React, { useState } from "react";
-import axios from "axios";
-import { useNavigate } from "react-router-dom";
-import "./Login.css";
+import React, { useState } from 'react';
+import axios from 'axios';
+import {useNavigate} from "react-router-dom";
+import './Login.css';
const Login = () => {
const [username, setUsername] = useState("");
diff --git a/webapp/src/components/Stats.js b/webapp/src/components/Stats.js
index 34e2b0fd..1c47483f 100644
--- a/webapp/src/components/Stats.js
+++ b/webapp/src/components/Stats.js
@@ -3,9 +3,10 @@ const axios = require('axios');
// Definir la función para realizar la solicitud
async function fetchStats() {
const username = localStorage.getItem('username');
- const url = 'http://localhost:8001/getstats';
+ const url = 'http://localhost:8004/stats';
const params = {
- user: username
+ user: username,
+ gamemode: "clasico"
};
try {
diff --git a/webapp/src/index.css b/webapp/src/index.css
index ec2585e8..bf6df07a 100644
--- a/webapp/src/index.css
+++ b/webapp/src/index.css
@@ -11,3 +11,11 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+
+#root {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
diff --git a/webapp/src/pages/Authenticate/Authenticate.css b/webapp/src/pages/Authenticate/Authenticate.css
index e1c7f780..7ebe727b 100644
--- a/webapp/src/pages/Authenticate/Authenticate.css
+++ b/webapp/src/pages/Authenticate/Authenticate.css
@@ -1,3 +1,16 @@
+.auth-container {
+ text-align: center;
+ margin-top: 50px;
+ }
+
+ .auth-title {
+ margin-bottom: 20px;
+ }
+
+ .auth-link {
+ margin-top: 20px;
+ }
+
.gotoregister,
.gotologin {
box-shadow: none;
diff --git a/webapp/src/pages/Authenticate/Authenticate.test.js b/webapp/src/pages/Authenticate/Authenticate.test.js
new file mode 100644
index 00000000..4a705cfa
--- /dev/null
+++ b/webapp/src/pages/Authenticate/Authenticate.test.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { render, fireEvent } from '@testing-library/react';
+import Authenticate from './Authenticate';
+
+test('renders welcome message', () => {
+ const { getByText } = render(
El usuario no ha jugado ninguna partida.
Usuario: | +Usuario | {stats.username} | |
Juegos Jugados: | +Partidas jugadas | {stats.nGamesPlayed} | |
Promedio de Puntos: | +Puntos por partida | {stats.avgPoints.toFixed(2)} | |
Puntos Totales: | +Puntos totales | {stats.totalPoints} | |
Preguntas Correctas Totales: | +Preguntas correctas totales | {stats.totalCorrectQuestions} | |
Preguntas Incorrectas Totales: | +Preguntas incorrectas totales | {stats.totalIncorrectQuestions} | |
Ratio Correctas/Incorrectas: | -{stats.ratioCorrectToIncorrect.toFixed(2)} | +Porcentaje de aciertos | +{stats.ratioCorrect.toFixed(2)}% |
Tiempo por pregunta (s): | {stats.avgTime.toFixed(2)} |
Usuario | +Puntos promedio | +
---|---|
{stat.username} | +{stat.avgPoints.toFixed(2)} | +