From dd3b9c88cd01d18bd42c12f53551d135c517bfac Mon Sep 17 00:00:00 2001 From: Matthew Grove Date: Sun, 3 Oct 2021 18:56:22 +0100 Subject: [PATCH] Add average mark to progress page & fix loader --- firestore.rules | 4 ++++ functions/index.js | 20 ++++++++++++++++++++ src/Progress.js | 30 ++++++++++++++++++++++++++---- test/firestore.test.js | 12 ++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/firestore.rules b/firestore.rules index e8f070f..e73e464 100644 --- a/firestore.rules +++ b/firestore.rules @@ -222,5 +222,9 @@ service cloud.firestore { match /join_codes/{joinCode} { allow get: if isSignedIn(); } + + match /completed_progress/{setIds} { + allow get: if isSignedIn(); + } } } diff --git a/functions/index.js b/functions/index.js index 6e8df5b..7a962fa 100644 --- a/functions/index.js +++ b/functions/index.js @@ -386,6 +386,7 @@ function cleanseVocabString(item) { * @param {object} data The data passed to the function. * @param {string} data.progressId The ID of the progress document to update. * @param {string} data.answer The answer given by the user to the current prompt. + * @return {string} averagePercentage The average percentage mark for the current collection of sets. Only returned when the test is complete. * @return {boolean} correct Whether the provided answer was correct. * @return {array} correctAnswers An array of correct answers for the question just answered. If not all correct * answers have yet been given, and the current answer is correct, this only contains the correct @@ -551,8 +552,27 @@ exports.processAnswer = functions.https.onCall((data, context) => { if (mode === "lives" && docData.lives <= 0) docData.questions.length = returnData.totalQuestions = docData.progress; + const completedProgressDocId = db.collection("completed_progress").doc(progressDoc.data().setIds.sort().join("__")); + return transaction.get(completedProgressDocId).then((completedProgressDoc) => { + const totalPercentage = completedProgressDoc.data().total_percentage + (docData.correct.length / docData.questions.length * 100); + const attempts = completedProgressDoc.data().attempts + 1; + transaction.set(completedProgressDocId, { + attempts: attempts, + total_percentage: totalPercentage, + }); + returnData.averagePercentage = (totalPercentage / attempts).toFixed(2); + transaction.set(progressDocId, docData); + return returnData; + }).catch((error) => { + const totalPercentage = docData.correct.length / docData.questions.length * 100; + transaction.set(completedProgressDocId, { + attempts: 1, + total_percentage: totalPercentage, + }); + returnData.averagePercentage = totalPercentage.toFixed(2); transaction.set(progressDocId, docData); return returnData; + }); } else { const nextVocabId = docData.questions[docData.progress]; const nextSetOwner = nextVocabId.split("__")[0]; diff --git a/src/Progress.js b/src/Progress.js index 7400d89..8434ec6 100644 --- a/src/Progress.js +++ b/src/Progress.js @@ -70,6 +70,8 @@ export default withRouter(class Progress extends React.Component { lives: 1, startLives: null, setComplete: false, + averagePercentage: null, + pageLoaded: false, }; let isMounted = true; @@ -87,7 +89,7 @@ export default withRouter(class Progress extends React.Component { const progressId = this.props.match.params.progressId; const progressRef = this.state.db.collection("progress").doc(progressId); - let [ newState, setDone, incorrectAnswers, duration ] = await progressRef.get().then((doc) => { + let [ newState, setDone, incorrectAnswers, duration, setIds ] = await progressRef.get().then((doc) => { const data = doc.data(); document.title = `Study | ${data.set_title} | Parandum`; @@ -107,6 +109,7 @@ export default withRouter(class Progress extends React.Component { setIds: data.setIds, originalTotalQuestions: [...new Set(data.questions)].length, setComplete: data.duration !== null, + pageLoaded: true, }; if (data.lives) { @@ -114,7 +117,7 @@ export default withRouter(class Progress extends React.Component { newState.startLives = data.start_lives; } - return [ newState, data.duration !== null, data.incorrect, data.duration ]; + return [ newState, data.duration !== null, data.incorrect, data.duration, data.setIds ]; }).catch((error) => { console.log(`Progress data inaccessible: ${error}`); return [ @@ -166,6 +169,16 @@ export default withRouter(class Progress extends React.Component { })); })); + promises.push(this.state.db.collection("completed_progress") + .doc(setIds.sort().join("__")) + .get() + .then((completedProgressDoc) => { + newState.averagePercentage = (completedProgressDoc.data().total_percentage / completedProgressDoc.data().attempts).toFixed(2); + }).catch((error) => { + console.log(`Couldn't get average percentage: ${error}`); + newState.averagePercentage = null; + })); + if (incorrectAnswers.length > 0) { newState.incorrectAnswers = {}; @@ -363,6 +376,7 @@ export default withRouter(class Progress extends React.Component { if (data.duration) { // test done newState.duration = data.duration; + newState.averagePercentage = data.averagePercentage; this.props.logEvent("test_complete", { progress_id: this.props.match.params.progressId, @@ -491,7 +505,8 @@ export default withRouter(class Progress extends React.Component { return (
{ - this.state.progressInaccessible + this.state.pageLoaded && + (this.state.progressInaccessible ? : @@ -570,6 +585,13 @@ export default withRouter(class Progress extends React.Component {

You got

{`${(this.state.correct / this.state.totalQuestions * 100).toFixed(2)}%`}

+ { + this.state.averagePercentage !== null && +
+

The average is

+

{`${this.state.averagePercentage}%`}

+
+ }

{`${this.state.correct} of ${this.state.totalQuestions}`}

marks

@@ -739,7 +761,7 @@ export default withRouter(class Progress extends React.Component {
} - + ) } ) diff --git a/test/firestore.test.js b/test/firestore.test.js index e1ca165..aaa4811 100644 --- a/test/firestore.test.js +++ b/test/firestore.test.js @@ -820,4 +820,16 @@ describe("Parandum Firestore database", () => { const testDoc = db.collection("join_codes").doc(joinCodeOne); await firebase.assertFails(testDoc.get()); }); + + it("Can read completed progress docs when signed in", async () => { + const db = getFirestore(myAuth); + const testDoc = db.collection("completed_progress").doc(setOne); + await firebase.assertSucceeds(testDoc.get()); + }); + + it("Can't read completed progress docs when not signed in", async () => { + const db = getFirestore(null); + const testDoc = db.collection("completed_progress").doc(setOne); + await firebase.assertFails(testDoc.get()); + }); }); \ No newline at end of file