Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lti problem progress #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"expr-eval": "~2.0.2",
"firebase": "~9.9.1",
"http2": "~3.3.7",
"jwt-decode": "^4.0.0",
"list-react-files": "~0.2.0",
"localforage": "~1.10.0",
"mathlive": "^0.98.3",
Expand Down
50 changes: 37 additions & 13 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import NotFound from "@components/NotFound.js";
import {
DO_FOCUS_TRACKING,
PROGRESS_STORAGE_KEY,
CANVAS_PROBLEM_SCORE_KEY,
SITE_VERSION,
ThemeContext,
USER_ID_STORAGE_KEY,
Expand Down Expand Up @@ -128,6 +129,7 @@ class App extends React.Component {
const user = parseJwt(additionalContext.jwt);
additionalContext["user"] = user;
additionalContext["studentName"] = user.full_name;
this.jwt = additionalContext.jwt;
}

// Firebase creation
Expand Down Expand Up @@ -235,24 +237,46 @@ class App extends React.Component {
);
})
);
const { setByKey } = this.browserStorage;
setByKey(PROGRESS_STORAGE_KEY, progressedBktParams, (err) => {
if (err) {
console.debug("save progress error: ", err);
toast.warn("Unable to save mastery progress :(", {
console.log(progressedBktParams)

if (this.jwt) {
const key = CANVAS_PROBLEM_SCORE_KEY(this.jwt);
this.firebase.setMastery(key, progressedBktParams)
.then((_) => {})
.catch((err) => {
toast.warn("Unable to save mastery progress to Firebase", {
toastId: "unable_to_save_progress",
});
} else {
console.debug("saved progress successfully");
}
}).then((_) => {});
});
} else {
const { setByKey } = this.browserStorage;
setByKey(PROGRESS_STORAGE_KEY, progressedBktParams, (err) => {
if (err) {
console.debug("save progress error: ", err);
toast.warn("Unable to save mastery progress :(", {
toastId: "unable_to_save_progress",
});
} else {
console.debug("saved progress successfully");
}
}).then((_) => {});
}
};

loadBktProgress = async () => {
const { getByKey } = this.browserStorage;
const progress = await getByKey(PROGRESS_STORAGE_KEY).catch((_e) => {
console.debug("error with getting previous progress", _e);
});
let progress;
if (this.jwt) {
const key = CANVAS_PROBLEM_SCORE_KEY(this.jwt);
progress = await this.firebase.getMastery(key).catch((_e) => {
console.debug("error with getting previous progress", _e);
});
} else {
const { getByKey } = this.browserStorage;
progress = await getByKey(PROGRESS_STORAGE_KEY).catch((_e) => {
console.debug("error with getting previous progress", _e);
});
}

if (
progress == null ||
typeof progress !== "object" ||
Expand Down
63 changes: 57 additions & 6 deletions src/components/Firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import {
import { initializeApp } from "firebase/app";
import {
arrayUnion,
connectFirestoreEmulator,
doc,
getFirestore,
serverTimestamp,
setDoc,
getDoc,
} from "firebase/firestore";
import daysSinceEpoch from "../util/daysSinceEpoch";
import {
Expand All @@ -24,6 +26,8 @@ import {
} from "../util/getBuildType";

const problemSubmissionsOutput = "problemSubmissions";
const canvasLessonProgress = "canvasLessonProgress";
const canvasProblemMastery = "canvasProblemMastery";
const problemStartLogOutput = "problemStartLogs";
const GPTExperimentOutput = "GPTExperimentOutput";
const feedbackOutput = "feedbacks";
Expand Down Expand Up @@ -106,7 +110,7 @@ class Firebase {
Document: Key - How you will access this data later. Usually username
Data: Value - JSON object of data you want to store
*/
async writeData(_collection, data) {
async writeData(_collection, data, docID = null) {
if (!ENABLE_FIREBASE) return;
const collection = this.getCollectionName(_collection);
const payload = this.addMetaData(data);
Expand All @@ -116,13 +120,14 @@ class Firebase {
console.debug("Writing this payload to firebase: ", payload);
}

docID = docID || this._getReadableID();

await setDoc(
doc(this.db, collection, this._getReadableID()),
doc(this.db, collection, docID),
payload
).catch((err) => {
console.log("a non-critical error occurred.");
console.log("Error is: ", err);
console.debug(err);
)
.catch((err) => {
console.log("Error while writing to Firebase: ", err);
});
}

Expand Down Expand Up @@ -329,6 +334,52 @@ class Firebase {
return this.writeData("mouseMovement", data);
}

async getCompletedProblems(docID) {
const collectionName = this.getCollectionName(canvasLessonProgress);
const docRef = doc(this.db, collectionName, docID);
const docSnapshot = await getDoc(docRef);

if (!docSnapshot.exists) {
throw new Error(`Document with ID ${docID} does not exist.`);
}

const data = docSnapshot.data();
if (!data || !data.problems) {
throw new Error(`Document with ID ${docID} does not contain "problems" field.`);
}

return data.problems;
}

async setCompletedProblems(docID, problems) {
const problemObject = {"problems": problems};
this.writeData(canvasLessonProgress, problemObject, docID);
}

async getMastery(docID) {
const collectionName = this.getCollectionName(canvasProblemMastery);
const docRef = doc(this.db, collectionName, docID);

const docSnapshot = await getDoc(docRef);

if (!docSnapshot.exists) {
throw new Error(`Document with ID ${docID} does not exist.`);
}

const data = docSnapshot.data();
if (!data) {
throw new Error(`Document with ID ${docID} does not contain "mastery" field.`);
}

console.log("got mastery from firebase: ", data.mastery);
return data.mastery;
}

async setMastery(docID, masteryObject) {
masteryObject = {"mastery": masteryObject};
this.writeData(canvasProblemMastery, masteryObject, docID);
}

startedProblem(problemID, courseName, lesson, lessonObjectives) {
if (!DO_LOG_DATA) return;
console.debug(
Expand Down
12 changes: 11 additions & 1 deletion src/config/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { jwtDecode } from 'jwt-decode';
import courses from "../content-sources/oatutor/coursePlans.json";
import { calculateSemester } from "../util/calculateSemester.js";

Expand Down Expand Up @@ -74,7 +75,16 @@ const USER_ID_STORAGE_KEY = `${_SHORT_SITE_NAME}-user_id`;
const PROGRESS_STORAGE_KEY = `${_SHORT_SITE_NAME}-progress`;
export const LESSON_PROGRESS_STORAGE_KEY = (lessonId) =>
`${PROGRESS_STORAGE_KEY}-${lessonId}`;

export const CANVAS_PROBLEM_SCORE_KEY = (jwtToken) => {
const decodedToken = jwtDecode(jwtToken);
const userId = decodedToken.user_id;
return `${PROGRESS_STORAGE_KEY}-${userId}`;
}
export const CANVAS_LESSON_PROGRESS_KEY = (jwtToken, lessonId) => {
const decodedToken = jwtDecode(jwtToken);
const userId = decodedToken.user_id;
return `${PROGRESS_STORAGE_KEY}-${userId}-${lessonId}`;
};
export const STEP_PROGRESS_STORAGE_KEY = () =>
`${PROGRESS_STORAGE_KEY}-steps`;

Expand Down
68 changes: 47 additions & 21 deletions src/platform-logic/Platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
coursePlans,
findLessonById,
LESSON_PROGRESS_STORAGE_KEY,
CANVAS_LESSON_PROGRESS_KEY,
STEP_PROGRESS_STORAGE_KEY,
MIDDLEWARE_URL,
SITE_NAME,
Expand Down Expand Up @@ -243,10 +244,15 @@ class Platform extends React.Component {
this.lesson = lesson;

const loadLessonProgress = async () => {
const { getByKey } = this.context.browserStorage;
return await getByKey(
LESSON_PROGRESS_STORAGE_KEY(this.lesson.id)
).catch((err) => {});
if (this.context?.jwt) {
const key = CANVAS_LESSON_PROGRESS_KEY(this.context.jwt, this.lesson.id);
return await this.context.firebase.getCompletedProblems(key).catch((err) => {});
} else {
const { getByKey } = this.context.browserStorage;
return await getByKey(
LESSON_PROGRESS_STORAGE_KEY(this.lesson.id)
).catch((err) => {});
}
};

const loadStepProgress = async () => {
Expand Down Expand Up @@ -337,23 +343,43 @@ class Platform extends React.Component {

problemComplete = async (context) => {
this.completedProbs.add(this.state.currProblem.id);
const { setByKey } = this.context.browserStorage;
await setByKey(
LESSON_PROGRESS_STORAGE_KEY(this.lesson.id),
this.completedProbs
).catch((error) => {
this.context.firebase.submitSiteLog(
"site-error",
`componentName: Platform.js`,
{
errorName: error.name || "n/a",
errorCode: error.code || "n/a",
errorMsg: error.message || "n/a",
errorStack: error.stack || "n/a",
},
this.state.currProblem.id
);
});
if (this.context?.jwt) {
const key = CANVAS_LESSON_PROGRESS_KEY(this.context.jwt, this.lesson.id)
await this.context.firebase.setCompletedProblems(
key,
Array.from(this.completedProbs)
).catch((error) => {
this.context.firebase.submitSiteLog(
"site-error",
`componentName: Platform.js`,
{
errorName: error.name || "n/a",
errorCode: error.code || "n/a",
errorMsg: error.message || "n/a",
errorStack: error.stack || "n/a",
},
this.state.currProblem.id
);
});
} else {
const { setByKey } = this.context.browserStorage;
await setByKey(
LESSON_PROGRESS_STORAGE_KEY(this.lesson.id),
this.completedProbs
).catch((error) => {
this.context.firebase.submitSiteLog(
"site-error",
`componentName: Platform.js`,
{
errorName: error.name || "n/a",
errorCode: error.code || "n/a",
errorMsg: error.message || "n/a",
errorStack: error.stack || "n/a",
},
this.state.currProblem.id
);
});
}
this._nextProblem(context);
};

Expand Down