From 308d87d5654be00322edd7063499362c4b92eec5 Mon Sep 17 00:00:00 2001 From: lukew3 Date: Tue, 25 May 2021 13:39:37 -0400 Subject: [PATCH] Working on checkTagPb, fixed keySpacingStats not saving err, --- backend/models/user.js | 4 +- backend/mongo-todo.md | 21 +++- backend/server.js | 191 +++++++++++++++++++++++++++---- src/js/db.js | 32 ++---- src/js/popups/edit-tags-popup.js | 1 - src/js/test/test-logic.js | 1 - 6 files changed, 199 insertions(+), 51 deletions(-) diff --git a/backend/models/user.js b/backend/models/user.js index b065cefe5..abd3ebd1b 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -3,7 +3,7 @@ const Schema = mongoose.Schema; const tagSchema = new Schema({ name: { type: String, required: true }, - //should be pb storage here i think + personalBests: { type: Schema.Types.Mixed }, }); const resultSchema = new Schema({ @@ -35,7 +35,7 @@ const resultSchema = new Schema({ err: [{ type: Number }], }, customText: { type: Schema.Types.Mixed }, - keySpacingStats: String, //not sure that this needs to exist, it's set as null in all of mine + keySpacingStats: { type: Schema.Types.Mixed }, //not sure that this needs to exist, it's set as null in all of mine name: { type: String, required: true }, //name of the user who took the test //should probably be typistName/username or something isPb: { type: Boolean, required: true }, }); diff --git a/backend/mongo-todo.md b/backend/mongo-todo.md index 20c9ca049..43918542a 100644 --- a/backend/mongo-todo.md +++ b/backend/mongo-todo.md @@ -1,9 +1,11 @@ +## Todo + +- Get google login working - Transfer leaderboard and other cloud functions - Reverse list of results on account page - spinning wheel on account page should dissapear after data is loaded - Account data should be updated when new result is added/test completed - Add email verification -- Tests started and completed doesn't increment when quitting a running test - Joined date doesn't look the same as it did before - Personal bests items should not be arrays - should be objects that are set on new pb @@ -11,17 +13,26 @@ - Result is duplicated in analytics - Does entire result need to be stored in analytics - Should result be stored in seperate collection and then referenced in user doc and analytics? -- Loader should hide after tag is added, deleted, or edited - Fix localhost, production, development server detection - - Should be a setting in the .env + - Maybe it could be set through package.json + - When a specific script is run, a certain mode will be activated +- Tests started and completed doesn't increment when quitting a running test + - Doesn't work as I expected in live version either, no issue + +## After beta is ready -- Are personal bests calculated from actual result data? - - Setting a low pb after resetting personal bests doesn't register on the scoreboard - make sure refresh token won't expire - make refresh token expire after session if don't remeber me is set? - Keep jwt and refresh in cookies? - Get somebody else to check over security due to my lack of expertise +- Work on transfering data from firebase to mongo + +## After release - Investigate and improve efficiency after mongo merge or during review +- I'm not sure about this but it might be worthwhile to use redis to store userdata up to a certain point + - Users who have been requested in the last hour will be stored in the redis database so that their data can be sent again without having to search a large database + - After an hour without a new request they can be removed from memory +- User data should not be requested from the server every time a test is submitted, result should just be appended to results diff --git a/backend/server.js b/backend/server.js index 4a3e7d492..46624c407 100644 --- a/backend/server.js +++ b/backend/server.js @@ -173,7 +173,7 @@ async function checkIfPB(uid, obj, userdata) { }); //checked all pbs, nothing found - meaning this is a new pb if (!found) { - pbs[obj.mode][obj.mode2].push({ + pbs[obj.mode][obj.mode2] = { language: obj.language, difficulty: obj.difficulty, punctuation: obj.punctuation, @@ -182,7 +182,7 @@ async function checkIfPB(uid, obj, userdata) { raw: obj.rawWpm, timestamp: Date.now(), consistency: obj.consistency, - }); + }; toUpdate = true; } } catch (e) { @@ -215,8 +215,159 @@ async function checkIfPB(uid, obj, userdata) { } async function checkIfTagPB(uid, obj, userdata) { - //Add functionality before release - return true; + //function returns a list of tag ids where a pb was set //i think + if (obj.tags.length === 0) { + return []; + } + if (obj.mode == "quote") { + return []; + } + let dbtags = []; //tags from database: include entire document: name, id, pbs + let restags = obj.tags; //result tags + try { + let snap; + await User.findOne({ name: userdata.name }, (err, user) => { + snap = user.tags; + }); + snap.forEach((doc) => { + //if (restags.includes(doc._id)) { + //if (restags.indexOf((doc._id).toString()) > -1) { + if (restags.includes(doc._id.toString())) { + //not sure what this is supposed to do + /* + let data = doc.data(); + data.id = doc.id; + dbtags.push(data); + */ + dbtags.push(doc); + } + }); + } catch { + return []; + } + let ret = []; + for (let i = 0; i < dbtags.length; i++) { + let pbs = null; + try { + pbs = dbtags[i].personalBests; + if (pbs === undefined || pbs === {}) { + throw new Error("pb is undefined"); + } + } catch (e) { + console.log("PBs undefined"); + //undefined personal best = new personal best + await User.findOne({ name: userdata.name }, (err, user) => { + //it might be more convenient if tags was an object with ids as the keys + for (let j = 0; j < user.tags.length; j++) { + console.log(user.tags[j]); + if (user.tags[j]._id.toString() == dbtags[i]._id.toString()) { + user.tags[j].personalBests = { + [obj.mode]: { + [obj.mode2]: { + language: obj.language, + difficulty: obj.difficulty, + punctuation: obj.punctuation, + wpm: obj.wpm, + acc: obj.acc, + raw: obj.rawWpm, + timestamp: Date.now(), + consistency: obj.consistency, + }, + }, + }; + } + pbs = user.tags[j].personalBests; + } + user.save(); + }).then((updatedUser) => { + ret.push(dbtags[i]._id.toString()); + }); + continue; + } + let toUpdate = false; + let found = false; + try { + if (pbs[obj.mode] === undefined) { + pbs[obj.mode] = { [obj.mode2]: [] }; + } else if (pbs[obj.mode][obj.mode2] === undefined) { + pbs[obj.mode][obj.mode2] = []; + } + pbs[obj.mode][obj.mode2].forEach((pb) => { + if ( + pb.punctuation === obj.punctuation && + pb.difficulty === obj.difficulty && + pb.language === obj.language + ) { + //entry like this already exists, compare wpm + found = true; + if (pb.wpm < obj.wpm) { + //new pb + pb.wpm = obj.wpm; + pb.acc = obj.acc; + pb.raw = obj.rawWpm; + pb.timestamp = Date.now(); + pb.consistency = obj.consistency; + toUpdate = true; + } else { + //no pb + return false; + } + } + }); + //checked all pbs, nothing found - meaning this is a new pb + if (!found) { + console.log("Semi-new pb"); + pbs[obj.mode][obj.mode2] = { + language: obj.language, + difficulty: obj.difficulty, + punctuation: obj.punctuation, + wpm: obj.wpm, + acc: obj.acc, + raw: obj.rawWpm, + timestamp: Date.now(), + consistency: obj.consistency, + }; + toUpdate = true; + } + } catch (e) { + // console.log(e); + console.log("Catch pb"); + console.log(e); + pbs[obj.mode] = {}; + pbs[obj.mode][obj.mode2] = [ + { + language: obj.language, + difficulty: obj.difficulty, + punctuation: obj.punctuation, + wpm: obj.wpm, + acc: obj.acc, + raw: obj.rawWpm, + timestamp: Date.now(), + consistency: obj.consistency, + }, + ]; + toUpdate = true; + } + + if (toUpdate) { + console.log("Adding new pb at end"); + await User.findOne({ name: userdata.name }, (err, user) => { + //it might be more convenient if tags was an object with ids as the keys + for (let j = 0; j < user.tags.length; j++) { + console.log(user.tags[j]); + console.log(dbtags[i]); + if (user.tags[j]._id.toString() === dbtags[i]._id.toString()) { + console.log("Made it inside the if"); + user.tags[j].personalBests = dbtags[i].personalBests; + } + } + user.save(); + }); + ret.push(dbtags[i]._id.toString()); + } + } + console.log(ret); + return ret; } async function stripAndSave(uid, obj) { @@ -495,7 +646,7 @@ app.get("/api/fetchSnapshot", authenticateToken, (req, res) => { let snap = user; delete snap.password; //return user data - res.json({ snap: snap }); + res.send({ snap: snap }); }); }); @@ -742,7 +893,7 @@ app.post("/api/testCompleted", authenticateToken, (req, res) => { // emailVerified // ), checkIfPB(request.uid, request.obj, userdata), - checkIfTagPB(request.uid, request.obj), + checkIfTagPB(request.uid, request.obj, userdata), ]) .then(async (values) => { // let globallb = values[0].insertedAt; @@ -1295,20 +1446,20 @@ app.post("/api/addTag", authenticateToken, (req, res) => { }) .then((updatedUser) => { console.log(`user ${req.name} created a tag: ${req.body.tagName}`); - return { + res.json({ resultCode: 1, id: updatedUser.tags[updatedUser.tags.length - 1]._id, - }; + }); }) .catch((e) => { console.error( `error while creating tag for user ${req.name}: ${e.message}` ); - return { resultCode: -999, message: e.message }; + res.json({ resultCode: -999, message: e.message }); }); } catch (e) { console.error(`error adding tag for ${req.name} - ${e}`); - return { resultCode: -999, message: e.message }; + res.json({ resultCode: -999, message: e.message }); } }); @@ -1326,19 +1477,17 @@ app.post("/api/editTag", authenticateToken, (req, res) => { }) .then((updatedUser) => { console.log(`user ${req.name} updated a tag: ${req.name}`); - return { - resultCode: 1, - }; + res.json({ resultCode: 1 }); }) .catch((e) => { console.error( `error while updating tag for user ${req.name}: ${e.message}` ); - return { resultCode: -999, message: e.message }; + res.json({ resultCode: -999, message: e.message }); }); } catch (e) { console.error(`error updating tag for ${req.name} - ${e}`); - return { resultCode: -999, message: e.message }; + res.json({ resultCode: -999, message: e.message }); } }); @@ -1355,17 +1504,15 @@ app.post("/api/removeTag", authenticateToken, (req, res) => { }) .then((updatedUser) => { console.log(`user ${req.name} deleted a tag`); - return { - resultCode: 1, - }; + res.json({ resultCode: 1 }); }) .catch((e) => { console.error(`error deleting tag for user ${req.name}: ${e.message}`); - return { resultCode: -999 }; + res.json({ resultCode: -999 }); }); } catch (e) { console.error(`error deleting tag for ${req.name} - ${e}`); - return { resultCode: -999 }; + res.json({ resultCode: -999 }); } }); @@ -1376,12 +1523,12 @@ app.post("/api/resetPersonalBests", authenticateToken, (req, res) => { user.personalBests = {}; user.save(); }); - return true; + res.status(200).send({ status: "Reset Pbs successfully" }); } catch (e) { console.log( `something went wrong when deleting personal bests for ${uid}: ${e.message}` ); - return false; + res.status(500).send({ status: "Reset Pbs successfully" }); } }); // ANALYTICS API diff --git a/src/js/db.js b/src/js/db.js index f95a8c66a..a0726cfb9 100644 --- a/src/js/db.js +++ b/src/js/db.js @@ -1,17 +1,11 @@ import { loadTags } from "./result-filters"; import * as AccountButton from "./account-button"; -import * as CloudFunctions from "./cloud-functions"; import * as Notifications from "./notifications"; import axiosInstance from "./axios-instance"; import Cookies from "js-cookie"; -//const db = firebase.firestore(); -//db.settings({ experimentalForceLongPolling: true }); - let dbSnapshot = null; -//I think that a lot of these functions could be simplified by api calls - export function updateName(uid, name) { //db.collection(`users`).doc(uid).set({ name: name }, { merge: true }); axiosInstance.post("/api/updateName", { @@ -57,7 +51,6 @@ export function currentUser() { export async function initSnapshot() { //send api request with token that returns tags, presets, and data needed for snap if (currentUser() == null) return false; - const token = Cookies.get("accessToken"); await axiosInstance .get("/api/fetchSnapshot") .then((response) => { @@ -335,21 +328,20 @@ export async function getLocalTagPB( ) { function cont() { let ret = 0; - let filteredtag = dbSnapshot.tags.filter((t) => t.id === tagId)[0]; + let filteredtag = dbSnapshot.tags.filter((t) => t._id === tagId)[0]; try { - filteredtag.personalBests[mode][mode2].forEach((pb) => { - if ( - pb.punctuation == punctuation && - pb.difficulty == difficulty && - pb.language == language - ) { - ret = pb.wpm; - } - }); - return ret; + const pb = filteredtag.personalBests[mode][mode2]; + if ( + pb.punctuation == punctuation && + pb.difficulty == difficulty && + pb.language == language + ) { + ret = pb.wpm; + } } catch (e) { - return ret; + console.log(e); } + return ret; } let retval; @@ -375,7 +367,7 @@ export async function saveLocalTagPB( ) { if (mode == "quote") return; function cont() { - let filteredtag = dbSnapshot.tags.filter((t) => t.id === tagId)[0]; + let filteredtag = dbSnapshot.tags.filter((t) => t._id === tagId)[0]; try { let found = false; if (filteredtag.personalBests[mode][mode2] === undefined) { diff --git a/src/js/popups/edit-tags-popup.js b/src/js/popups/edit-tags-popup.js index dc9bcfb24..c5c14f17a 100644 --- a/src/js/popups/edit-tags-popup.js +++ b/src/js/popups/edit-tags-popup.js @@ -2,7 +2,6 @@ import * as ResultTagsPopup from "./result-tags-popup"; import * as ResultFilters from "./result-filters"; import * as Loader from "./loader"; import * as DB from "./db"; -import * as CloudFunctions from "./cloud-functions"; import * as Notifications from "./notifications"; import * as Settings from "./settings"; import axiosInstance from "./axios-instance"; diff --git a/src/js/test/test-logic.js b/src/js/test/test-logic.js index 189eb498f..7370652f5 100644 --- a/src/js/test/test-logic.js +++ b/src/js/test/test-logic.js @@ -25,7 +25,6 @@ import * as OutOfFocus from "./out-of-focus"; import * as AccountButton from "./account-button"; import * as DB from "./db"; import * as ThemeColors from "./theme-colors"; -import * as CloudFunctions from "./cloud-functions"; import * as TestLeaderboards from "./test-leaderboards"; import * as Replay from "./replay.js"; import axiosInstance from "./axios-instance";