From 9bb9152df0ae57edfa6d8abc0628f68875ee7d39 Mon Sep 17 00:00:00 2001 From: Evan Welsh <2943271+ewlsh@users.noreply.github.com> Date: Sat, 3 Oct 2020 13:38:14 -0500 Subject: [PATCH 01/16] Switch primary branch to main. --- .github/workflows/cd.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 6797cbf39..e191afa52 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,7 +2,7 @@ name: CD on: push: branches: - - master + - main jobs: deploy: diff --git a/README.md b/README.md index 80900c3ee..74beb50d3 100644 --- a/README.md +++ b/README.md @@ -72,5 +72,5 @@ _Screenshots showing major parts of app_ We are a team within **Cornell Design & Tech Initiative**. For more information, see our [website](https://cornelldti.org/). - + ​ From f43cef0f25883ce64b59340a13b8e936c15eb067 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Sat, 9 Nov 2024 21:53:21 -0500 Subject: [PATCH 02/16] added month and day fields --- src/scripts/wrapped-fa24.js | 370 ++++++++++++++++++++++++++++++++++++ src/scripts/wrapped-fa24.ts | 279 +++++++++++++++++++++------ 2 files changed, 593 insertions(+), 56 deletions(-) create mode 100644 src/scripts/wrapped-fa24.js diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js new file mode 100644 index 000000000..d67453c88 --- /dev/null +++ b/src/scripts/wrapped-fa24.js @@ -0,0 +1,370 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +var firebase_admin_1 = require("firebase-admin"); +firebase_admin_1["default"].initializeApp({ + credential: firebase_admin_1["default"].credential.applicationDefault(), + databaseURL: 'https://qmi-test.firebaseio.com' +}); +// eslint-disable-next-line no-console +console.log('Firebase admin initialized!'); +// Initialize Firestore +var db = firebase_admin_1["default"].firestore(); +var errorUsers = []; +// Firestore Timestamps for the query range +var startDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-01-22')); +var endDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-11-22')); +var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function () { + var questionsRef, sessionsRef, wrappedRef, usersRef, questionsSnapshot, userStats, getWrappedUserDocs, taCounts, officeHourSessions, TAsessions, updateWrappedDocs, initializeUser, _loop_1, _i, _a, doc, _loop_2, _b, _c, _d, userId, stats; + var _e, _f, _g, _h, _j, _k, _l, _m, _o; + return __generator(this, function (_p) { + switch (_p.label) { + case 0: + questionsRef = db.collection('questions-test'); + sessionsRef = db.collection('sessions-test'); + wrappedRef = db.collection('wrapped-fa24'); + usersRef = db.collection('users-test'); + return [4 /*yield*/, questionsRef + .where('timeEntered', '>=', startDate) + .where('timeEntered', '<=', endDate) + .get()]; + case 1: + questionsSnapshot = _p.sent(); + userStats = {}; + getWrappedUserDocs = function () { return __awaiter(void 0, void 0, void 0, function () { + var docs; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + docs = {}; + return [4 /*yield*/, Promise.all(Object.keys(userStats).map(function (id) { return __awaiter(void 0, void 0, void 0, function () { + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!id) return [3 /*break*/, 2]; + _a = docs; + _b = id; + return [4 /*yield*/, usersRef.doc(id).get()]; + case 1: + _a[_b] = _c.sent(); + _c.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }))]; + case 1: + _a.sent(); + return [2 /*return*/, docs]; + } + }); + }); }; + taCounts = {}; + officeHourSessions = {}; + TAsessions = {}; + updateWrappedDocs = function () { return __awaiter(void 0, void 0, void 0, function () { + var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, hasFavoriteTa, wrappedDocRef, userDoc; + var _c; + return __generator(this, function (_d) { + switch (_d.label) { + case 0: + batch = db.batch(); + return [4 /*yield*/, getWrappedUserDocs()]; + case 1: + userDocuments = _d.sent(); + for (_i = 0, _a = Object.entries(userStats); _i < _a.length; _i++) { + _b = _a[_i], userId = _b[0], stats = _b[1]; + // Only want to make wrapped changes for a user if they have an ID and are active + if (userId) { + hasVisits = stats.numVisits > 0; + isUserActive = stats.timeHelpingStudents === undefined || (((_c = TAsessions[userId]) === null || _c === void 0 ? void 0 : _c.length) > 0); + hasFavoriteTa = stats.favTaId !== ""; + if (hasVisits && isUserActive && hasFavoriteTa) { + wrappedDocRef = wrappedRef.doc(userId); + batch.set(wrappedDocRef, stats); + userDoc = userDocuments[userId]; + if (userDoc.exists) { + usersRef.doc(userId).update({ + wrapped: true + }); + } + else { + // Handle the case where the document does not exist + errorUsers.push({ user: userId, error: "No document found for this user, skipping update." }); + } + } + else { + errorUsers.push({ user: userId, error: "User is not an active student/TA" }); + } + } + else { + errorUsers.push({ user: userId, error: "User ID is undefined, skipping update." }); + } + } + return [4 /*yield*/, batch.commit()]; + case 2: + _d.sent(); + return [2 /*return*/]; + } + }); + }); }; + initializeUser = function (answererId, askerId) { + var _a; + // if an instance doesn't exist yet for the user, creating one + if (!userStats[askerId]) { + userStats[askerId] = { + numVisits: 0, + favClass: '', + favTaId: '', + totalMinutes: 0, + personalityType: '' + }; + taCounts[askerId] = new Map(); + officeHourSessions[askerId] = []; + } + if (!userStats[answererId]) { + userStats[answererId] = { + numVisits: 0, + favClass: '', + favTaId: '', + totalMinutes: 0, + personalityType: '', + timeHelpingStudents: 0 + }; + taCounts[answererId] = new Map(); + officeHourSessions[answererId] = []; + TAsessions[answererId] = []; + // Checking if ta already showed up as student and now as an answerer + } + else if (userStats[answererId] && ((_a = userStats[answererId]) === null || _a === void 0 ? void 0 : _a.timeHelpingStudents) === undefined) { + userStats[answererId] = { + numVisits: userStats[answererId].numVisits, + favClass: userStats[answererId].favClass, + favTaId: userStats[answererId].favTaId, + totalMinutes: userStats[answererId].totalMinutes, + personalityType: userStats[answererId].personalityType, + timeHelpingStudents: 0 + }; + taCounts[answererId] = new Map(); + officeHourSessions[answererId] = []; + TAsessions[answererId] = []; + } + }; + _loop_1 = function (doc) { + var question, answererId, askerId, sessionId, timeEntered, timeAddressed, sessionDoc, timeHelping, taAmt, minutesSpent; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + question = doc.data(); + answererId = question.answererId, askerId = question.askerId, sessionId = question.sessionId, timeEntered = question.timeEntered, timeAddressed = question.timeAddressed; + initializeUser(answererId, askerId); + return [4 /*yield*/, sessionsRef.doc(sessionId).get()]; + case 1: + sessionDoc = _a.sent(); + if (TAsessions[answererId].find(function (TAsession) { return TAsession.session === sessionId; }) === undefined) { + /* Since TA was active during this session and this is the first + time encountering the session, we add it to their timeHelped */ + if (sessionDoc.exists && userStats[answererId].timeHelpingStudents !== undefined) { + timeHelping = (sessionDoc.get('endTime').toDate().getTime() + - sessionDoc.get('startTime').toDate().getTime()) / 60000; + // this should never be less than 0 (or 0, really) + if (timeHelping >= 0) { + userStats[answererId].timeHelpingStudents = + ((_e = userStats[answererId].timeHelpingStudents) !== null && _e !== void 0 ? _e : 0) + timeHelping; + } + } + } + officeHourSessions[askerId] = officeHourSessions[askerId] || []; + if (!officeHourSessions[askerId].includes(sessionId)) { + officeHourSessions[askerId].push(sessionId); + } + if (answererId !== undefined && answererId !== "") { + // eslint-disable-next-line no-console + console.log(sessionDoc.get('startTime').toDate()); + // eslint-disable-next-line no-console + console.log(sessionDoc.get('startTime').toDate().getDay()); + // eslint-disable-next-line no-console + console.log(sessionDoc.get('startTime').toDate().getMonth()); + // eslint-disable-next-line no-console + console.log("-------"); + (_f = TAsessions[answererId]) === null || _f === void 0 ? void 0 : _f.push({ + session: sessionId, + asker: askerId, + courseId: sessionDoc.get('courseId'), + day: sessionDoc.get('startTime').toDate().getDay() + }); + if (!((_g = taCounts[askerId]) === null || _g === void 0 ? void 0 : _g.has(answererId))) { + (_h = taCounts[askerId]) === null || _h === void 0 ? void 0 : _h.set(answererId, 1); + } + else if (answererId !== undefined && ((_j = taCounts[askerId]) === null || _j === void 0 ? void 0 : _j.has(answererId))) { + taAmt = (_k = taCounts[askerId]) === null || _k === void 0 ? void 0 : _k.get(answererId); + taAmt && ((_l = taCounts[askerId]) === null || _l === void 0 ? void 0 : _l.set(answererId, taAmt + 1)); + } + } + // Minutes spent at office hours + if (timeEntered) { + if (timeAddressed) { + minutesSpent = (timeAddressed.toDate().getTime() - + timeEntered.toDate().getTime()) / 60000; + if (minutesSpent >= 0) { + userStats[askerId].totalMinutes += minutesSpent; + } + } + else { + userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed + } + } + return [2 /*return*/]; + } + }); + }; + _i = 0, _a = questionsSnapshot.docs; + _p.label = 2; + case 2: + if (!(_i < _a.length)) return [3 /*break*/, 5]; + doc = _a[_i]; + return [5 /*yield**/, _loop_1(doc)]; + case 3: + _p.sent(); + _p.label = 4; + case 4: + _i++; + return [3 /*break*/, 2]; + case 5: + _loop_2 = function (userId, stats) { + var weeksInRange, averageSessionsPerWeek, resSession, sessionsDoc, sessionFrequency_1, modeSessionId, sessionsDoc; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + stats.numVisits = (_m = officeHourSessions[userId]) === null || _m === void 0 ? void 0 : _m.length; + stats.totalMinutes = Math.ceil(stats.totalMinutes); + if (stats.timeHelpingStudents !== undefined) { + stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + } + weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) + / (1000 * 60 * 60 * 24 * 7); + averageSessionsPerWeek = stats.numVisits / weeksInRange; + if (averageSessionsPerWeek >= 2) { + stats.personalityType = 'Consistent'; + } + else if (averageSessionsPerWeek >= 0.5) { + stats.personalityType = 'Resourceful'; + } + else { + stats.personalityType = 'Independent'; + } + // Get the ids in the map that have the highest counts + if (taCounts[userId].size !== 0) { + stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (a, b) { return a[1] < b[1] ? b : a; })[0]; + } + if (!(stats.favTaId && stats.favTaId !== "")) return [3 /*break*/, 4]; + resSession = (_o = TAsessions[stats.favTaId]) === null || _o === void 0 ? void 0 : _o.filter(function (TAsession) { + return officeHourSessions[userId].includes(TAsession.session); + }); + if (!((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1)) return [3 /*break*/, 2]; + return [4 /*yield*/, sessionsRef.doc(resSession[0].session).get()]; + case 1: + sessionsDoc = _a.sent(); + stats.favClass = sessionsDoc.get("courseId"); + return [3 /*break*/, 4]; + case 2: + if (!((resSession === null || resSession === void 0 ? void 0 : resSession.length) > 1)) return [3 /*break*/, 4]; + sessionFrequency_1 = {}; + resSession.filter(function (elem) { return elem.asker === userId; }).forEach(function (elem) { + if (!sessionFrequency_1[elem.session]) { + sessionFrequency_1[elem.session] = 1; + } + else { + sessionFrequency_1[elem.session] += 1; + } + }); + modeSessionId = Object.keys(sessionFrequency_1).reduce(function (a, b) { + return sessionFrequency_1[a] > sessionFrequency_1[b] ? a : b; + }); + return [4 /*yield*/, sessionsRef.doc(modeSessionId).get()]; + case 3: + sessionsDoc = _a.sent(); + stats.favClass = sessionsDoc.get("courseId"); + _a.label = 4; + case 4: return [2 /*return*/]; + } + }); + }; + _b = 0, _c = Object.entries(userStats); + _p.label = 6; + case 6: + if (!(_b < _c.length)) return [3 /*break*/, 9]; + _d = _c[_b], userId = _d[0], stats = _d[1]; + return [5 /*yield**/, _loop_2(userId, stats)]; + case 7: + _p.sent(); + _p.label = 8; + case 8: + _b++; + return [3 /*break*/, 6]; + case 9: return [4 /*yield*/, updateWrappedDocs()]; + case 10: + _p.sent(); + // debugging console log + // eslint-disable-next-line no-console + errorUsers.forEach(function (errUser) { return console.log(errUser.user + ": " + errUser.error); }); + return [2 /*return*/]; + } + }); +}); }; +(function () { return __awaiter(void 0, void 0, void 0, function () { + var error_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _a.trys.push([0, 2, , 3]); + return [4 /*yield*/, getWrapped()]; + case 1: + _a.sent(); + // eslint-disable-next-line no-console + console.log("Processing complete."); + return [3 /*break*/, 3]; + case 2: + error_1 = _a.sent(); + // eslint-disable-next-line no-console + console.error("Failed to update stats:", error_1); + return [3 /*break*/, 3]; + case 3: return [2 /*return*/]; + } + }); +}); })(); diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index f628c3543..78ca1e401 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -9,94 +9,248 @@ admin.initializeApp({ // eslint-disable-next-line no-console console.log('Firebase admin initialized!'); + + // Initialize Firestore const db = admin.firestore(); +const errorUsers: { + user: string, + error: string +}[] = []; // Firestore Timestamps for the query range -const startDate = admin.firestore.Timestamp.fromDate(new Date('2023-08-20')); -const endDate = admin.firestore.Timestamp.fromDate(new Date('2024-05-19')); + +const startDate = admin.firestore.Timestamp.fromDate(new Date('2024-01-22')); +const endDate = admin.firestore.Timestamp.fromDate(new Date('2024-11-22')); const getWrapped = async () => { // Refs - const questionsRef = db.collection('questions'); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const sessionsRef = db.collection('sessions'); - const wrappedRef = db.collection('wrapped'); - const usersRef = db.collection('users'); + const questionsRef = db.collection('questions-test'); + const sessionsRef = db.collection('sessions-test'); + const wrappedRef = db.collection('wrapped-fa24'); + const usersRef = db.collection('users-test'); // Query all questions asked between FA23 and SP24 const questionsSnapshot = await questionsRef - .where('timeEntered', '>=', startDate) + .where('timeEntered', '>=', startDate) .where('timeEntered', '<=', endDate) .get(); const userStats: { [userId: string]: { - officeHourVisits: string[]; + numVisits: number; + favClass: string; + favTaId: string; + favMonth: number; + favDay: number; totalMinutes: number; personalityType: string; timeHelpingStudents?: number; }} = {}; - const TAsessions: { [userId: string]: string[]} = {}; + const getWrappedUserDocs = async () => { + const docs: {[userId:string]: FirebaseFirestore.DocumentSnapshot} = {}; + await Promise.all(Object.keys(userStats).map(async (id) => { + if (id){ + docs[id] = await usersRef.doc(id).get(); + } + } + )); + return docs; + } - for (const doc of questionsSnapshot.docs) { - const question = doc.data() as { - answererId: string; - askerId: string; - sessionId: string; - timeEntered: admin.firestore.Timestamp; - timeAddressed: admin.firestore.Timestamp | undefined; - }; - const { answererId, askerId, sessionId, timeEntered, timeAddressed } = question; + const taCounts: {[userId: string] : Map} = {}; + const officeHourSessions: { [userId: string]: string[]} = {}; + // Every taID has an array of objects, where the objects store a sessionId and askerId + const TAsessions: {[taID:string]: { + session: string; + asker: string; + courseId: string; + day: number; + month: number; + + }[]} = {}; + // Helper functions + const updateWrappedDocs = async () => { + // Update the wrapped collection + const batch = db.batch(); + const userDocuments = await getWrappedUserDocs(); + for (const [userId, stats] of Object.entries(userStats)) { + // Only want to make wrapped changes for a user if they have an ID and are active + if (userId) { + /* Defition of active: + If a user is only a student, they need to have at least one OH visit. + If a user is a TA, they need to have at least one TA session AND at least one OH visit as a student. + */ + + const hasVisits = stats.numVisits > 0; + // This is true if the user is either a student, or a TA who has more than one session + const isUserActive = stats.timeHelpingStudents === undefined || (TAsessions[userId]?.length > 0); + const hasFavoriteTa = stats.favTaId !== ""; + if (hasVisits && isUserActive && hasFavoriteTa) { + const wrappedDocRef = wrappedRef.doc(userId); + batch.set(wrappedDocRef, stats); + + // eslint-disable-next-line no-await-in-loop + // const userDoc = await usersRef.doc(userId).get(); + const userDoc = userDocuments[userId]; + if (userDoc.exists) { + usersRef.doc(userId).update({ + wrapped: true, + }); + } else { + // Handle the case where the document does not exist + errorUsers.push({user: userId, error: "No document found for this user, skipping update."}); + } + } else { + errorUsers.push({user: userId, error: "User is not an active student/TA"}); + } + + } else { + errorUsers.push({user: userId, error: "User ID is undefined, skipping update."}); + } + } + + await batch.commit(); + } + + const initializeUser = (answererId:string, askerId:string) => { + // if an instance doesn't exist yet for the user, creating one if (!userStats[askerId]) { userStats[askerId] = { - officeHourVisits: [], + numVisits: 0, + favClass: '', + favTaId: '', + favMonth: 0, + favDay: 0, totalMinutes: 0, personalityType: '', }; - } + + taCounts[askerId] = new Map(); + officeHourSessions[askerId] = []; + } if (!userStats[answererId]) { userStats[answererId] = { - officeHourVisits: [], + numVisits: 0, + favClass: '', + favTaId: '', + favMonth: 0, + favDay: 0, totalMinutes: 0, personalityType: '', timeHelpingStudents: 0, }; + + taCounts[answererId] = new Map(); + officeHourSessions[answererId] = []; + TAsessions[answererId] = []; + // Checking if ta already showed up as student and now as an answerer + } else if (userStats[answererId] && userStats[answererId]?.timeHelpingStudents === undefined) { + userStats[answererId] = { + numVisits: userStats[answererId].numVisits, + favClass: userStats[answererId].favClass, + favTaId: userStats[answererId].favTaId, + favMonth: userStats[answererId].favMonth, + favDay: userStats[answererId].favMonth, + totalMinutes: userStats[answererId].totalMinutes, + personalityType: userStats[answererId].personalityType, + timeHelpingStudents: 0, + }; + + taCounts[answererId] = new Map(); + officeHourSessions[answererId] = []; + TAsessions[answererId] = []; } + } + + // do all sorting for preprocessing here + + for (const doc of questionsSnapshot.docs) { + const question = doc.data() as { + answererId: string; + askerId: string; + sessionId: string; + timeEntered: admin.firestore.Timestamp; + timeAddressed: admin.firestore.Timestamp | undefined; + }; + + const { answererId, askerId, sessionId, timeEntered, timeAddressed } = question; + + initializeUser(answererId, askerId); + // Office hour visits - if (!userStats[askerId].officeHourVisits?.includes(sessionId)) { - userStats[askerId].officeHourVisits?.push(sessionId); + // TRYING TO FIX BELOW LINE + // eslint-disable-next-line no-await-in-loop + const sessionDoc = await sessionsRef.doc(sessionId).get(); + if (TAsessions[answererId].find((TAsession) => TAsession.session === sessionId) === undefined) { + /* Since TA was active during this session and this is the first + time encountering the session, we add it to their timeHelped */ + + if (sessionDoc.exists && userStats[answererId].timeHelpingStudents !== undefined ) { + /* Add a total session time to the min TA helped */ + const timeHelping = (sessionDoc.get('endTime').toDate().getTime() + - sessionDoc.get('startTime').toDate().getTime())/ 60000; + // this should never be less than 0 (or 0, really) + if (timeHelping >= 0) { + userStats[answererId].timeHelpingStudents = + (userStats[answererId].timeHelpingStudents ?? 0) + timeHelping; + } + } + } + + officeHourSessions[askerId] = officeHourSessions[askerId] || []; + if (!officeHourSessions[askerId].includes(sessionId)) { + officeHourSessions[askerId].push(sessionId); } + + if (answererId !== undefined && answererId !== "") { + TAsessions[answererId]?.push({ + session: sessionId, + asker: askerId, + courseId: sessionDoc.get('courseId'), + day: sessionDoc.get('startTime').toDate().getDay(), + month: sessionDoc.get('startTime').toDate().getMonth() + }); + + if(!taCounts[askerId]?.has(answererId)) { + taCounts[askerId]?.set(answererId, 1); + } else if (answererId !== undefined && taCounts[askerId]?.has(answererId)){ + const taAmt = taCounts[askerId]?.get(answererId); + taAmt && taCounts[askerId]?.set(answererId, taAmt + 1 ); + } } // Minutes spent at office hours if (timeEntered) { if (timeAddressed) { const minutesSpent = (timeAddressed.toDate().getTime() - - timeEntered.toDate().getTime()) / 60000; // convert ms to minutes - userStats[askerId].totalMinutes += minutesSpent; + timeEntered.toDate().getTime()) / 60000; // convert ms to minutes + if (minutesSpent >= 0) { + userStats[askerId].totalMinutes += minutesSpent; + } } else { userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed } } - - if (!TAsessions[answererId]?.includes(sessionId)) { - TAsessions[answererId]?.push(sessionId); - } } // Personality type will be calculated after processing all documents // Process personality type - for (const [, stats] of Object.entries(userStats)) { - const sessionCount = stats.officeHourVisits.length; + for (const [userId, stats] of Object.entries(userStats)) { + stats.numVisits = officeHourSessions[userId]?.length; + stats.totalMinutes = Math.ceil(stats.totalMinutes); + if (stats.timeHelpingStudents !== undefined) { + stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + } + const weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks - const averageSessionsPerWeek = sessionCount / weeksInRange; + const averageSessionsPerWeek = stats.numVisits / weeksInRange; if (averageSessionsPerWeek >= 2) { stats.personalityType = 'Consistent'; @@ -105,34 +259,47 @@ const getWrapped = async () => { } else { stats.personalityType = 'Independent'; } - } - - // Update the wrapped collection - const batch = db.batch(); + + // Get the ids in the map that have the highest counts + if (taCounts[userId].size !== 0) { + stats.favTaId = Array.from(taCounts[userId].entries()).reduce((a, b) => a[1] < b[1] ? b : a)[0]; + } - Object.entries(userStats).forEach(async ([userId, stats]) => { - if (userId) { - const wrappedDocRef = wrappedRef.doc(userId); - batch.set(wrappedDocRef, stats); + if (stats.favTaId && stats.favTaId !== "") { + const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => + officeHourSessions[userId].includes(TAsession.session)); + if (resSession?.length === 1) { + // eslint-disable-next-line no-await-in-loop + const sessionsDoc = await sessionsRef.doc(resSession[0].session).get() + stats.favClass = sessionsDoc.get("courseId"); - const userDoc = await usersRef.doc(userId).get(); - if (userDoc.exists) { - usersRef.doc(userId).update({ - wrapped: true, + } else if (resSession?.length > 1) { + // finding session that occurs the most + const sessionFrequency: { [courseId: string]: number } = {}; + + resSession.filter((elem) => elem.asker === userId).forEach((elem) => { + if (!sessionFrequency[elem.session]) { + sessionFrequency[elem.session] = 1; + } else { + sessionFrequency[elem.session] += 1; + } }); - } else { - // Handle the case where the document does not exist - // eslint-disable-next-line no-console - console.log(`No document found for user ID ${userId}, skipping update.`); + + const modeSessionId = Object.keys(sessionFrequency).reduce((a, b) => + sessionFrequency[a] > sessionFrequency[b] ? a : b); + // eslint-disable-next-line no-await-in-loop + const sessionsDoc = await sessionsRef.doc(modeSessionId).get() + stats.favClass = sessionsDoc.get("courseId"); } - } else { - // eslint-disable-next-line no-console - console.log("User ID is undefined, skipping update.") } - - }); + } + + await updateWrappedDocs(); + // debugging console log + // eslint-disable-next-line no-console + errorUsers.forEach((errUser) => console.log(errUser.user + ": " + errUser.error)); - await batch.commit(); + } (async () => { @@ -144,4 +311,4 @@ const getWrapped = async () => { // eslint-disable-next-line no-console console.error("Failed to update stats:", error); } -})(); +})(); \ No newline at end of file From abe758bfc63eeeb494e88d6ba8c093f941542f83 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Sun, 10 Nov 2024 14:50:38 -0500 Subject: [PATCH 03/16] added most time spent in a month logic - added some extra checks and made some anon functions have more clear names --- src/scripts/wrapped-fa24.js | 89 ++++++++++++++++++++++--------------- src/scripts/wrapped-fa24.ts | 70 ++++++++++++++++++----------- 2 files changed, 97 insertions(+), 62 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index d67453c88..8ccd9b5ae 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -50,10 +50,10 @@ var errorUsers = []; var startDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-01-22')); var endDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-11-22')); var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function () { - var questionsRef, sessionsRef, wrappedRef, usersRef, questionsSnapshot, userStats, getWrappedUserDocs, taCounts, officeHourSessions, TAsessions, updateWrappedDocs, initializeUser, _loop_1, _i, _a, doc, _loop_2, _b, _c, _d, userId, stats; - var _e, _f, _g, _h, _j, _k, _l, _m, _o; - return __generator(this, function (_p) { - switch (_p.label) { + var questionsRef, sessionsRef, wrappedRef, usersRef, questionsSnapshot, userStats, getWrappedUserDocs, taCounts, monthTimeCounts, officeHourSessions, TAsessions, updateWrappedDocs, initializeUser, _loop_1, _i, _a, doc, _loop_2, _b, _c, _d, userId, stats; + var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; + return __generator(this, function (_q) { + switch (_q.label) { case 0: questionsRef = db.collection('questions-test'); sessionsRef = db.collection('sessions-test'); @@ -64,7 +64,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function .where('timeEntered', '<=', endDate) .get()]; case 1: - questionsSnapshot = _p.sent(); + questionsSnapshot = _q.sent(); userStats = {}; getWrappedUserDocs = function () { return __awaiter(void 0, void 0, void 0, function () { var docs; @@ -95,6 +95,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function }); }); }; taCounts = {}; + monthTimeCounts = {}; officeHourSessions = {}; TAsessions = {}; updateWrappedDocs = function () { return __awaiter(void 0, void 0, void 0, function () { @@ -115,17 +116,22 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function isUserActive = stats.timeHelpingStudents === undefined || (((_c = TAsessions[userId]) === null || _c === void 0 ? void 0 : _c.length) > 0); hasFavoriteTa = stats.favTaId !== ""; if (hasVisits && isUserActive && hasFavoriteTa) { - wrappedDocRef = wrappedRef.doc(userId); - batch.set(wrappedDocRef, stats); - userDoc = userDocuments[userId]; - if (userDoc.exists) { - usersRef.doc(userId).update({ - wrapped: true - }); + if (stats.favClass && stats.favDay && stats.favMonth) { + errorUsers.push({ user: userId, error: "User is active and has favorite TA, but is missing either most common class, day, or month." }); } else { - // Handle the case where the document does not exist - errorUsers.push({ user: userId, error: "No document found for this user, skipping update." }); + wrappedDocRef = wrappedRef.doc(userId); + batch.set(wrappedDocRef, stats); + userDoc = userDocuments[userId]; + if (userDoc.exists) { + usersRef.doc(userId).update({ + wrapped: true + }); + } + else { + // Handle the case where the document does not exist + errorUsers.push({ user: userId, error: "No document found for this user, skipping update." }); + } } } else { @@ -151,17 +157,22 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numVisits: 0, favClass: '', favTaId: '', + favMonth: 0, + favDay: 0, totalMinutes: 0, personalityType: '' }; taCounts[askerId] = new Map(); officeHourSessions[askerId] = []; + monthTimeCounts[askerId] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; } if (!userStats[answererId]) { userStats[answererId] = { numVisits: 0, favClass: '', favTaId: '', + favMonth: 0, + favDay: 0, totalMinutes: 0, personalityType: '', timeHelpingStudents: 0 @@ -169,6 +180,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function taCounts[answererId] = new Map(); officeHourSessions[answererId] = []; TAsessions[answererId] = []; + monthTimeCounts[answererId] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // Checking if ta already showed up as student and now as an answerer } else if (userStats[answererId] && ((_a = userStats[answererId]) === null || _a === void 0 ? void 0 : _a.timeHelpingStudents) === undefined) { @@ -176,13 +188,16 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numVisits: userStats[answererId].numVisits, favClass: userStats[answererId].favClass, favTaId: userStats[answererId].favTaId, + favMonth: userStats[answererId].favMonth, + favDay: userStats[answererId].favMonth, totalMinutes: userStats[answererId].totalMinutes, personalityType: userStats[answererId].personalityType, timeHelpingStudents: 0 }; - taCounts[answererId] = new Map(); - officeHourSessions[answererId] = []; + // taCounts[answererId] = new Map(); + // officeHourSessions[answererId] = []; TAsessions[answererId] = []; + // monthTimeCounts[answererId] = [0,0,0,0,0,0,0,0,0,0,0,0]; } }; _loop_1 = function (doc) { @@ -213,25 +228,18 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function if (!officeHourSessions[askerId].includes(sessionId)) { officeHourSessions[askerId].push(sessionId); } - if (answererId !== undefined && answererId !== "") { - // eslint-disable-next-line no-console - console.log(sessionDoc.get('startTime').toDate()); - // eslint-disable-next-line no-console - console.log(sessionDoc.get('startTime').toDate().getDay()); - // eslint-disable-next-line no-console - console.log(sessionDoc.get('startTime').toDate().getMonth()); - // eslint-disable-next-line no-console - console.log("-------"); + if (answererId && timeAddressed) { (_f = TAsessions[answererId]) === null || _f === void 0 ? void 0 : _f.push({ session: sessionId, asker: askerId, courseId: sessionDoc.get('courseId'), - day: sessionDoc.get('startTime').toDate().getDay() + day: timeAddressed.toDate().getDay(), + month: sessionDoc.get('startTime').toDate().getMonth() }); if (!((_g = taCounts[askerId]) === null || _g === void 0 ? void 0 : _g.has(answererId))) { (_h = taCounts[askerId]) === null || _h === void 0 ? void 0 : _h.set(answererId, 1); } - else if (answererId !== undefined && ((_j = taCounts[askerId]) === null || _j === void 0 ? void 0 : _j.has(answererId))) { + else if (answererId && ((_j = taCounts[askerId]) === null || _j === void 0 ? void 0 : _j.has(answererId))) { taAmt = (_k = taCounts[askerId]) === null || _k === void 0 ? void 0 : _k.get(answererId); taAmt && ((_l = taCounts[askerId]) === null || _l === void 0 ? void 0 : _l.set(answererId, taAmt + 1)); } @@ -244,30 +252,34 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function if (minutesSpent >= 0) { userStats[askerId].totalMinutes += minutesSpent; } + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += minutesSpent; } else { userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } + // eslint-disable-next-line no-console + console.log(monthTimeCounts[askerId]); } return [2 /*return*/]; } }); }; _i = 0, _a = questionsSnapshot.docs; - _p.label = 2; + _q.label = 2; case 2: if (!(_i < _a.length)) return [3 /*break*/, 5]; doc = _a[_i]; return [5 /*yield**/, _loop_1(doc)]; case 3: - _p.sent(); - _p.label = 4; + _q.sent(); + _q.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: _loop_2 = function (userId, stats) { - var weeksInRange, averageSessionsPerWeek, resSession, sessionsDoc, sessionFrequency_1, modeSessionId, sessionsDoc; + var weeksInRange, averageSessionsPerWeek, modeElement, resSession, sessionsDoc, sessionFrequency_1, modeSessionId, sessionsDoc; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -288,12 +300,15 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function else { stats.personalityType = 'Independent'; } + // Month user spent the most time in + stats.favMonth = (_o = monthTimeCounts[userId]) === null || _o === void 0 ? void 0 : _o.indexOf(Math.max.apply(Math, monthTimeCounts[userId])); // Get the ids in the map that have the highest counts if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (a, b) { return a[1] < b[1] ? b : a; })[0]; + stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prev, next) { return prev[1] < next[1] ? next : prev; })[0]; } + modeElement = 0; if (!(stats.favTaId && stats.favTaId !== "")) return [3 /*break*/, 4]; - resSession = (_o = TAsessions[stats.favTaId]) === null || _o === void 0 ? void 0 : _o.filter(function (TAsession) { + resSession = (_p = TAsessions[stats.favTaId]) === null || _p === void 0 ? void 0 : _p.filter(function (TAsession) { return officeHourSessions[userId].includes(TAsession.session); }); if (!((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1)) return [3 /*break*/, 2]; @@ -326,20 +341,20 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function }); }; _b = 0, _c = Object.entries(userStats); - _p.label = 6; + _q.label = 6; case 6: if (!(_b < _c.length)) return [3 /*break*/, 9]; _d = _c[_b], userId = _d[0], stats = _d[1]; return [5 /*yield**/, _loop_2(userId, stats)]; case 7: - _p.sent(); - _p.label = 8; + _q.sent(); + _q.label = 8; case 8: _b++; return [3 /*break*/, 6]; case 9: return [4 /*yield*/, updateWrappedDocs()]; case 10: - _p.sent(); + _q.sent(); // debugging console log // eslint-disable-next-line no-console errorUsers.forEach(function (errUser) { return console.log(errUser.user + ": " + errUser.error); }); diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index 78ca1e401..91e6b57db 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -60,6 +60,7 @@ const getWrapped = async () => { const taCounts: {[userId: string] : Map} = {}; + const monthTimeCounts: {[userId: string]: number[]} = {}; const officeHourSessions: { [userId: string]: string[]} = {}; // Every taID has an array of objects, where the objects store a sessionId and askerId const TAsessions: {[taID:string]: { @@ -89,24 +90,27 @@ const getWrapped = async () => { const isUserActive = stats.timeHelpingStudents === undefined || (TAsessions[userId]?.length > 0); const hasFavoriteTa = stats.favTaId !== ""; if (hasVisits && isUserActive && hasFavoriteTa) { - const wrappedDocRef = wrappedRef.doc(userId); - batch.set(wrappedDocRef, stats); - - // eslint-disable-next-line no-await-in-loop - // const userDoc = await usersRef.doc(userId).get(); - const userDoc = userDocuments[userId]; - if (userDoc.exists) { - usersRef.doc(userId).update({ - wrapped: true, - }); + if (stats.favClass && stats.favDay && stats.favMonth) { + errorUsers.push({user: userId, error: "User is active and has favorite TA, but is missing either most common class, day, or month."}); } else { - // Handle the case where the document does not exist - errorUsers.push({user: userId, error: "No document found for this user, skipping update."}); + const wrappedDocRef = wrappedRef.doc(userId); + batch.set(wrappedDocRef, stats); + + // eslint-disable-next-line no-await-in-loop + // const userDoc = await usersRef.doc(userId).get(); + const userDoc = userDocuments[userId]; + if (userDoc.exists) { + usersRef.doc(userId).update({ + wrapped: true, + }); + } else { + // Handle the case where the document does not exist + errorUsers.push({user: userId, error: "No document found for this user, skipping update."}); + } } } else { - errorUsers.push({user: userId, error: "User is not an active student/TA"}); + errorUsers.push({user: userId, error: "User is not an active student/TA or doesn't have favorite TA."}); } - } else { errorUsers.push({user: userId, error: "User ID is undefined, skipping update."}); } @@ -130,6 +134,7 @@ const getWrapped = async () => { taCounts[askerId] = new Map(); officeHourSessions[askerId] = []; + monthTimeCounts[askerId] = [0,0,0,0,0,0,0,0,0,0,0,0]; } if (!userStats[answererId]) { @@ -147,6 +152,7 @@ const getWrapped = async () => { taCounts[answererId] = new Map(); officeHourSessions[answererId] = []; TAsessions[answererId] = []; + monthTimeCounts[answererId] = [0,0,0,0,0,0,0,0,0,0,0,0]; // Checking if ta already showed up as student and now as an answerer } else if (userStats[answererId] && userStats[answererId]?.timeHelpingStudents === undefined) { userStats[answererId] = { @@ -160,9 +166,10 @@ const getWrapped = async () => { timeHelpingStudents: 0, }; - taCounts[answererId] = new Map(); - officeHourSessions[answererId] = []; + // taCounts[answererId] = new Map(); + // officeHourSessions[answererId] = []; TAsessions[answererId] = []; + // monthTimeCounts[answererId] = [0,0,0,0,0,0,0,0,0,0,0,0]; } } @@ -206,18 +213,18 @@ const getWrapped = async () => { if (!officeHourSessions[askerId].includes(sessionId)) { officeHourSessions[askerId].push(sessionId); } - if (answererId !== undefined && answererId !== "") { + if (answererId && timeAddressed) { TAsessions[answererId]?.push({ session: sessionId, asker: askerId, courseId: sessionDoc.get('courseId'), - day: sessionDoc.get('startTime').toDate().getDay(), + day: timeAddressed.toDate().getDay(), month: sessionDoc.get('startTime').toDate().getMonth() }); if(!taCounts[askerId]?.has(answererId)) { taCounts[askerId]?.set(answererId, 1); - } else if (answererId !== undefined && taCounts[askerId]?.has(answererId)){ + } else if (answererId && taCounts[askerId]?.has(answererId)){ const taAmt = taCounts[askerId]?.get(answererId); taAmt && taCounts[askerId]?.set(answererId, taAmt + 1 ); } @@ -230,24 +237,28 @@ const getWrapped = async () => { timeEntered.toDate().getTime()) / 60000; // convert ms to minutes if (minutesSpent >= 0) { userStats[askerId].totalMinutes += minutesSpent; - } + } + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += minutesSpent; } else { userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } + // eslint-disable-next-line no-console + console.log(monthTimeCounts[askerId]); } } // Personality type will be calculated after processing all documents - // Process personality type + // Process stats for (const [userId, stats] of Object.entries(userStats)) { stats.numVisits = officeHourSessions[userId]?.length; stats.totalMinutes = Math.ceil(stats.totalMinutes); if (stats.timeHelpingStudents !== undefined) { stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); } - + // Personality type const weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks const averageSessionsPerWeek = stats.numVisits / weeksInRange; @@ -259,13 +270,18 @@ const getWrapped = async () => { } else { stats.personalityType = 'Independent'; } - + + // Month user spent the most time in + stats.favMonth = monthTimeCounts[userId]?.indexOf(Math.max(...monthTimeCounts[userId])); // Get the ids in the map that have the highest counts if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce((a, b) => a[1] < b[1] ? b : a)[0]; + stats.favTaId = Array.from(taCounts[userId].entries()).reduce((prev, next) => prev[1] < next[1] ? next : prev)[0]; } + const modeElement = 0; + if (stats.favTaId && stats.favTaId !== "") { + // only looking at the sessions from the favorite TA that match with sessions the user went to const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => officeHourSessions[userId].includes(TAsession.session)); if (resSession?.length === 1) { @@ -274,7 +290,11 @@ const getWrapped = async () => { stats.favClass = sessionsDoc.get("courseId"); } else if (resSession?.length > 1) { - // finding session that occurs the most + /* filtering from general to specific: + - find mode class + - out of all the sessions for that class, find favorite day to go + */ + const sessionFrequency: { [courseId: string]: number } = {}; resSession.filter((elem) => elem.asker === userId).forEach((elem) => { From ff9a3d6d016df2806ab15ad5949a2885ae340832 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Tue, 12 Nov 2024 19:10:01 -0500 Subject: [PATCH 04/16] stats seems to work, need to do further testing --- src/scripts/wrapped-fa24.js | 219 ++++++++++++++++++++++-------------- src/scripts/wrapped-fa24.ts | 143 ++++++++++++++++------- 2 files changed, 239 insertions(+), 123 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index 8ccd9b5ae..18c075cfc 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -99,7 +99,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function officeHourSessions = {}; TAsessions = {}; updateWrappedDocs = function () { return __awaiter(void 0, void 0, void 0, function () { - var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, hasFavoriteTa, wrappedDocRef, userDoc; + var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, hasFavoriteTa, taStatsMismatched, wrappedDocRef, userDoc; var _c; return __generator(this, function (_d) { switch (_d.label) { @@ -115,9 +115,14 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function hasVisits = stats.numVisits > 0; isUserActive = stats.timeHelpingStudents === undefined || (((_c = TAsessions[userId]) === null || _c === void 0 ? void 0 : _c.length) > 0); hasFavoriteTa = stats.favTaId !== ""; + taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) + || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); if (hasVisits && isUserActive && hasFavoriteTa) { - if (stats.favClass && stats.favDay && stats.favMonth) { - errorUsers.push({ user: userId, error: "User is active and has favorite TA, but is missing either most common class, day, or month." }); + if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { + errorUsers.push({ user: userId, error: "User is active and has favorite TA but missing one of the following:\n favClass: " + stats.favClass + ", favDay: " + stats.favDay + ", favMonth: " + stats.favMonth }); + } + else if (taStatsMismatched) { + errorUsers.push({ user: userId, error: "Mismatch in updating ta specfic values." }); } else { wrappedDocRef = wrappedRef.doc(userId); @@ -135,7 +140,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function } } else { - errorUsers.push({ user: userId, error: "User is not an active student/TA" }); + errorUsers.push({ user: userId, error: "User is not an active student/TA or doesn't have favorite TA." }); } } else { @@ -157,8 +162,8 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numVisits: 0, favClass: '', favTaId: '', - favMonth: 0, - favDay: 0, + favMonth: -1, + favDay: -1, totalMinutes: 0, personalityType: '' }; @@ -171,11 +176,12 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numVisits: 0, favClass: '', favTaId: '', - favMonth: 0, - favDay: 0, + favMonth: -1, + favDay: -1, totalMinutes: 0, personalityType: '', - timeHelpingStudents: 0 + timeHelpingStudents: 0, + numStudentsHelped: 0 }; taCounts[answererId] = new Map(); officeHourSessions[answererId] = []; @@ -189,15 +195,13 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function favClass: userStats[answererId].favClass, favTaId: userStats[answererId].favTaId, favMonth: userStats[answererId].favMonth, - favDay: userStats[answererId].favMonth, + favDay: userStats[answererId].favDay, totalMinutes: userStats[answererId].totalMinutes, personalityType: userStats[answererId].personalityType, - timeHelpingStudents: 0 + timeHelpingStudents: 0, + numStudentsHelped: 0 }; - // taCounts[answererId] = new Map(); - // officeHourSessions[answererId] = []; TAsessions[answererId] = []; - // monthTimeCounts[answererId] = [0,0,0,0,0,0,0,0,0,0,0,0]; } }; _loop_1 = function (doc) { @@ -233,8 +237,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function session: sessionId, asker: askerId, courseId: sessionDoc.get('courseId'), - day: timeAddressed.toDate().getDay(), - month: sessionDoc.get('startTime').toDate().getMonth() + day: timeAddressed.toDate().getDay() }); if (!((_g = taCounts[askerId]) === null || _g === void 0 ? void 0 : _g.has(answererId))) { (_h = taCounts[askerId]) === null || _h === void 0 ? void 0 : _h.set(answererId, 1); @@ -259,7 +262,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } // eslint-disable-next-line no-console - console.log(monthTimeCounts[askerId]); + console.log("month counts: [" + monthTimeCounts[askerId] + "]"); } return [2 /*return*/]; } @@ -279,81 +282,131 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function return [3 /*break*/, 2]; case 5: _loop_2 = function (userId, stats) { - var weeksInRange, averageSessionsPerWeek, modeElement, resSession, sessionsDoc, sessionFrequency_1, modeSessionId, sessionsDoc; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - stats.numVisits = (_m = officeHourSessions[userId]) === null || _m === void 0 ? void 0 : _m.length; - stats.totalMinutes = Math.ceil(stats.totalMinutes); - if (stats.timeHelpingStudents !== undefined) { - stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); - } - weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) - / (1000 * 60 * 60 * 24 * 7); - averageSessionsPerWeek = stats.numVisits / weeksInRange; - if (averageSessionsPerWeek >= 2) { - stats.personalityType = 'Consistent'; - } - else if (averageSessionsPerWeek >= 0.5) { - stats.personalityType = 'Resourceful'; + // eslint-disable-next-line no-console + console.log("name is " + userId); + stats.numVisits = (_m = officeHourSessions[userId]) === null || _m === void 0 ? void 0 : _m.length; + stats.totalMinutes = Math.ceil(stats.totalMinutes); + if (stats.timeHelpingStudents !== undefined) { + stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + if (stats.numStudentsHelped !== undefined) { + stats.numStudentsHelped = TAsessions[userId].length; + } + } + // Personality type + var weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) + / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks + var averageSessionsPerWeek = stats.numVisits / weeksInRange; + if (averageSessionsPerWeek >= 2) { + stats.personalityType = 'Consistent'; + } + else if (averageSessionsPerWeek >= 0.5) { + stats.personalityType = 'Resourceful'; + } + else { + stats.personalityType = 'Independent'; + } + // Month user spent the most time in + stats.favMonth = (_o = monthTimeCounts[userId]) === null || _o === void 0 ? void 0 : _o.indexOf(Math.max.apply(Math, monthTimeCounts[userId])); + // Get the ids in the map that have the highest counts + if (taCounts[userId].size !== 0) { + stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prev, next) { return prev[1] < next[1] ? next : prev; })[0]; + } + // eslint-disable-next-line no-console + console.log(userId + "'s fav ta is " + stats.favTaId + ", taCounts length is " + taCounts[userId].size); + if (stats.favTaId) { + // eslint-disable-next-line no-console + console.log("here"); + // only looking at the sessions from the favorite TA that match with sessions the user went to + var resSession = (_p = TAsessions[stats.favTaId]) === null || _p === void 0 ? void 0 : _p.filter(function (TAsession) { + return officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId; + }); + if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1) { + stats.favClass = resSession[0].courseId; + stats.favDay = resSession[0].day; + // eslint-disable-next-line no-console + console.log("finding " + userId + "'s mode stats"); + // eslint-disable-next-line no-console + console.log(stats.favTaId + "'s total sessions"); + // eslint-disable-next-line no-console + console.log(TAsessions[stats.favTaId]); + // eslint-disable-next-line no-console + console.log(stats.favTaId + ("'s total sessions with " + userId)); + // eslint-disable-next-line no-console + resSession.map(function (x) { return console.log(x); }); + } + else if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) > 1) { + /* filtering from general to specific: + - find mode class + - out of all the sessions for that class, find mode day + */ + // eslint-disable-next-line no-console + console.log("finding " + userId + "'s mode stats"); + // eslint-disable-next-line no-console + console.log(stats.favTaId + "'s total sessions"); + // eslint-disable-next-line no-console + console.log(TAsessions[stats.favTaId]); + // eslint-disable-next-line no-console + console.log(stats.favTaId + ("'s total sessions with " + userId)); + // eslint-disable-next-line no-console + resSession.map(function (x) { return console.log(x); }); + var classFrequency_1 = {}; + var dayFrequency_1 = {}; + /* + You need to find modeDay AFTER filtering all of modeCourse + because of the case where if you filter by looking at what + sessions have modeCourse AND modeDay, the modeDay might be a + number that doesn't exist with modeCourse. + */ + resSession.forEach(function (TAsession) { + if (!classFrequency_1[TAsession.courseId]) { + classFrequency_1[TAsession.courseId] = 1; } else { - stats.personalityType = 'Independent'; + classFrequency_1[TAsession.courseId] += 1; } - // Month user spent the most time in - stats.favMonth = (_o = monthTimeCounts[userId]) === null || _o === void 0 ? void 0 : _o.indexOf(Math.max.apply(Math, monthTimeCounts[userId])); - // Get the ids in the map that have the highest counts - if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prev, next) { return prev[1] < next[1] ? next : prev; })[0]; - } - modeElement = 0; - if (!(stats.favTaId && stats.favTaId !== "")) return [3 /*break*/, 4]; - resSession = (_p = TAsessions[stats.favTaId]) === null || _p === void 0 ? void 0 : _p.filter(function (TAsession) { - return officeHourSessions[userId].includes(TAsession.session); - }); - if (!((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1)) return [3 /*break*/, 2]; - return [4 /*yield*/, sessionsRef.doc(resSession[0].session).get()]; - case 1: - sessionsDoc = _a.sent(); - stats.favClass = sessionsDoc.get("courseId"); - return [3 /*break*/, 4]; - case 2: - if (!((resSession === null || resSession === void 0 ? void 0 : resSession.length) > 1)) return [3 /*break*/, 4]; - sessionFrequency_1 = {}; - resSession.filter(function (elem) { return elem.asker === userId; }).forEach(function (elem) { - if (!sessionFrequency_1[elem.session]) { - sessionFrequency_1[elem.session] = 1; + }); + var modeCourseId_1 = Object.keys(classFrequency_1).reduce(function (a, b) { + return classFrequency_1[a] > classFrequency_1[b] ? a : b; + }); + resSession.forEach(function (TAsession) { + if (TAsession.courseId === modeCourseId_1) { + if (!dayFrequency_1[TAsession.day]) { + dayFrequency_1[TAsession.day] = 1; } else { - sessionFrequency_1[elem.session] += 1; + dayFrequency_1[TAsession.day] += 1; } - }); - modeSessionId = Object.keys(sessionFrequency_1).reduce(function (a, b) { - return sessionFrequency_1[a] > sessionFrequency_1[b] ? a : b; - }); - return [4 /*yield*/, sessionsRef.doc(modeSessionId).get()]; - case 3: - sessionsDoc = _a.sent(); - stats.favClass = sessionsDoc.get("courseId"); - _a.label = 4; - case 4: return [2 /*return*/]; + } + }); + var modeDay_1 = Object.keys(dayFrequency_1).reduce(function (day1, day2) { + return dayFrequency_1[parseInt(day1, 10)] > dayFrequency_1[parseInt(day2, 10)] ? day1 : day2; + }); + // eslint-disable-next-line no-console + console.log("modeCourse: " + modeCourseId_1 + " and modeDay: " + modeDay_1); + var modeSessions = resSession.filter(function (TAsession) { return TAsession.courseId === modeCourseId_1 + && TAsession.day === parseInt(modeDay_1, 10); }); + // eslint-disable-next-line no-console + console.log("final filtering for modeSessions"); + // eslint-disable-next-line no-console + console.log(modeSessions); + // there could be multiple ties, so just picking the first one + stats.favClass = modeSessions[0].courseId; + stats.favDay = modeSessions[0].day; + // eslint-disable-next-line no-console + console.log("favClass: " + stats.favClass + ", favDay: " + stats.favDay); + // eslint-disable-next-line no-console + console.log("------------"); } - }); + } }; - _b = 0, _c = Object.entries(userStats); - _q.label = 6; + // Personality type will be calculated after processing all documents + // Process stats + for (_b = 0, _c = Object.entries(userStats); _b < _c.length; _b++) { + _d = _c[_b], userId = _d[0], stats = _d[1]; + _loop_2(userId, stats); + } + return [4 /*yield*/, updateWrappedDocs()]; case 6: - if (!(_b < _c.length)) return [3 /*break*/, 9]; - _d = _c[_b], userId = _d[0], stats = _d[1]; - return [5 /*yield**/, _loop_2(userId, stats)]; - case 7: - _q.sent(); - _q.label = 8; - case 8: - _b++; - return [3 /*break*/, 6]; - case 9: return [4 /*yield*/, updateWrappedDocs()]; - case 10: _q.sent(); // debugging console log // eslint-disable-next-line no-console diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index 91e6b57db..cfceab655 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -45,6 +45,7 @@ const getWrapped = async () => { totalMinutes: number; personalityType: string; timeHelpingStudents?: number; + numStudentsHelped?: number; }} = {}; const getWrappedUserDocs = async () => { @@ -68,8 +69,6 @@ const getWrapped = async () => { asker: string; courseId: string; day: number; - month: number; - }[]} = {}; // Helper functions @@ -89,15 +88,19 @@ const getWrapped = async () => { // This is true if the user is either a student, or a TA who has more than one session const isUserActive = stats.timeHelpingStudents === undefined || (TAsessions[userId]?.length > 0); const hasFavoriteTa = stats.favTaId !== ""; + const taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) + || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); if (hasVisits && isUserActive && hasFavoriteTa) { - if (stats.favClass && stats.favDay && stats.favMonth) { - errorUsers.push({user: userId, error: "User is active and has favorite TA, but is missing either most common class, day, or month."}); + if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { + errorUsers.push({user: userId, + error: `User is active and has favorite TA but missing one of the following: + favClass: ${stats.favClass}, favDay: ${stats.favDay}, favMonth: ${stats.favMonth}`}); + } else if (taStatsMismatched) { + errorUsers.push({user: userId, error: "Mismatch in updating ta specfic values."}) } else { const wrappedDocRef = wrappedRef.doc(userId); batch.set(wrappedDocRef, stats); - // eslint-disable-next-line no-await-in-loop - // const userDoc = await usersRef.doc(userId).get(); const userDoc = userDocuments[userId]; if (userDoc.exists) { usersRef.doc(userId).update({ @@ -126,8 +129,8 @@ const getWrapped = async () => { numVisits: 0, favClass: '', favTaId: '', - favMonth: 0, - favDay: 0, + favMonth: -1, + favDay: -1, totalMinutes: 0, personalityType: '', }; @@ -142,11 +145,12 @@ const getWrapped = async () => { numVisits: 0, favClass: '', favTaId: '', - favMonth: 0, - favDay: 0, + favMonth: -1, + favDay: -1, totalMinutes: 0, personalityType: '', timeHelpingStudents: 0, + numStudentsHelped: 0 }; taCounts[answererId] = new Map(); @@ -160,16 +164,13 @@ const getWrapped = async () => { favClass: userStats[answererId].favClass, favTaId: userStats[answererId].favTaId, favMonth: userStats[answererId].favMonth, - favDay: userStats[answererId].favMonth, + favDay: userStats[answererId].favDay, totalMinutes: userStats[answererId].totalMinutes, personalityType: userStats[answererId].personalityType, timeHelpingStudents: 0, + numStudentsHelped: 0 }; - - // taCounts[answererId] = new Map(); - // officeHourSessions[answererId] = []; TAsessions[answererId] = []; - // monthTimeCounts[answererId] = [0,0,0,0,0,0,0,0,0,0,0,0]; } } @@ -218,8 +219,7 @@ const getWrapped = async () => { session: sessionId, asker: askerId, courseId: sessionDoc.get('courseId'), - day: timeAddressed.toDate().getDay(), - month: sessionDoc.get('startTime').toDate().getMonth() + day: timeAddressed.toDate().getDay() }); if(!taCounts[askerId]?.has(answererId)) { @@ -244,7 +244,7 @@ const getWrapped = async () => { monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } // eslint-disable-next-line no-console - console.log(monthTimeCounts[askerId]); + console.log("month counts: [" + monthTimeCounts[askerId] + "]"); } } @@ -253,10 +253,15 @@ const getWrapped = async () => { // Process stats for (const [userId, stats] of Object.entries(userStats)) { + // eslint-disable-next-line no-console + console.log(`name is ${userId}`); stats.numVisits = officeHourSessions[userId]?.length; stats.totalMinutes = Math.ceil(stats.totalMinutes); if (stats.timeHelpingStudents !== undefined) { stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + if (stats.numStudentsHelped !== undefined) { + stats.numStudentsHelped = TAsessions[userId].length; + } } // Personality type const weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) @@ -275,41 +280,99 @@ const getWrapped = async () => { stats.favMonth = monthTimeCounts[userId]?.indexOf(Math.max(...monthTimeCounts[userId])); // Get the ids in the map that have the highest counts if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce((prev, next) => prev[1] < next[1] ? next : prev)[0]; + stats.favTaId = Array.from(taCounts[userId].entries()).reduce( + (prev, next) => prev[1] < next[1] ? next : prev)[0]; } - - const modeElement = 0; - - if (stats.favTaId && stats.favTaId !== "") { + // eslint-disable-next-line no-console + console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); + if (stats.favTaId) { + // eslint-disable-next-line no-console + console.log(`here`); // only looking at the sessions from the favorite TA that match with sessions the user went to const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => - officeHourSessions[userId].includes(TAsession.session)); + officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId); if (resSession?.length === 1) { - // eslint-disable-next-line no-await-in-loop - const sessionsDoc = await sessionsRef.doc(resSession[0].session).get() - stats.favClass = sessionsDoc.get("courseId"); - + stats.favClass = resSession[0].courseId; + stats.favDay = resSession[0].day; + // eslint-disable-next-line no-console + console.log(`finding ${userId}'s mode stats`); + // eslint-disable-next-line no-console + console.log(stats.favTaId + "'s total sessions"); + // eslint-disable-next-line no-console + console.log(TAsessions[stats.favTaId]); + // eslint-disable-next-line no-console + console.log(stats.favTaId + `'s total sessions with ${userId}`); + // eslint-disable-next-line no-console + resSession.map((x) => console.log(x)); } else if (resSession?.length > 1) { /* filtering from general to specific: - find mode class - - out of all the sessions for that class, find favorite day to go + - out of all the sessions for that class, find mode day */ - const sessionFrequency: { [courseId: string]: number } = {}; - - resSession.filter((elem) => elem.asker === userId).forEach((elem) => { - if (!sessionFrequency[elem.session]) { - sessionFrequency[elem.session] = 1; + // eslint-disable-next-line no-console + console.log(`finding ${userId}'s mode stats`); + // eslint-disable-next-line no-console + console.log(stats.favTaId + "'s total sessions"); + // eslint-disable-next-line no-console + console.log(TAsessions[stats.favTaId]); + // eslint-disable-next-line no-console + console.log(stats.favTaId + `'s total sessions with ${userId}`); + // eslint-disable-next-line no-console + resSession.map((x) => console.log(x)); + + + const classFrequency: { [courseId: string]: number } = {}; + const dayFrequency: { [day: number]: number } = {}; + + /* + You need to find modeDay AFTER filtering all of modeCourse + because of the case where if you filter by looking at what + sessions have modeCourse AND modeDay, the modeDay might be a + number that doesn't exist with modeCourse. + */ + + resSession.forEach((TAsession) => { + if (!classFrequency[TAsession.courseId]) { + classFrequency[TAsession.courseId] = 1; } else { - sessionFrequency[elem.session] += 1; + classFrequency[TAsession.courseId] += 1; } }); - const modeSessionId = Object.keys(sessionFrequency).reduce((a, b) => - sessionFrequency[a] > sessionFrequency[b] ? a : b); - // eslint-disable-next-line no-await-in-loop - const sessionsDoc = await sessionsRef.doc(modeSessionId).get() - stats.favClass = sessionsDoc.get("courseId"); + + const modeCourseId = Object.keys(classFrequency).reduce((a, b) => + classFrequency[a] > classFrequency[b] ? a : b); + resSession.forEach((TAsession) => { + if (TAsession.courseId === modeCourseId) { + if (!dayFrequency[TAsession.day]) { + dayFrequency[TAsession.day] = 1; + } else { + dayFrequency[TAsession.day] += 1; + } + } + }); + const modeDay = Object.keys(dayFrequency).reduce((day1, day2) => + dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2); + // eslint-disable-next-line no-console + console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); + const modeSessions = resSession.filter((TAsession) => TAsession.courseId === modeCourseId + && TAsession.day === parseInt(modeDay,10)); + + // eslint-disable-next-line no-console + console.log("final filtering for modeSessions"); + // eslint-disable-next-line no-console + console.log(modeSessions); + // there could be multiple ties, so just picking the first one + stats.favClass = modeSessions[0].courseId; + stats.favDay = modeSessions[0].day; + // eslint-disable-next-line no-console + console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); + + // eslint-disable-next-line no-console + console.log("------------"); + + } } } From b5bc1747b49846ad0182755b3fb3a43b9f5b902d Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Thu, 14 Nov 2024 17:39:25 -0500 Subject: [PATCH 05/16] refactored one more function and added progress statements for big loops --- src/scripts/wrapped-fa24.js | 414 +++++++++++++++++++----------------- src/scripts/wrapped-fa24.ts | 296 +++++++++++++------------- 2 files changed, 366 insertions(+), 344 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index 18c075cfc..bcc55c514 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -50,21 +50,21 @@ var errorUsers = []; var startDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-01-22')); var endDate = firebase_admin_1["default"].firestore.Timestamp.fromDate(new Date('2024-11-22')); var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function () { - var questionsRef, sessionsRef, wrappedRef, usersRef, questionsSnapshot, userStats, getWrappedUserDocs, taCounts, monthTimeCounts, officeHourSessions, TAsessions, updateWrappedDocs, initializeUser, _loop_1, _i, _a, doc, _loop_2, _b, _c, _d, userId, stats; - var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p; - return __generator(this, function (_q) { - switch (_q.label) { + var questionsRef, sessionsRef, wrappedRef, usersRef, questionsSnapshot, userStats, getWrappedUserDocs, getWrappedSessionDocs, taCounts, monthTimeCounts, officeHourSessions, TAsessions, updateWrappedDocs, initializeUser, processStats, sessionDocs, count, _loop_1, _i, _a, doc; + var _b, _c, _d, _e, _f, _g, _h; + return __generator(this, function (_j) { + switch (_j.label) { case 0: - questionsRef = db.collection('questions-test'); - sessionsRef = db.collection('sessions-test'); + questionsRef = db.collection('questions'); + sessionsRef = db.collection('sessions'); wrappedRef = db.collection('wrapped-fa24'); - usersRef = db.collection('users-test'); + usersRef = db.collection('users'); return [4 /*yield*/, questionsRef .where('timeEntered', '>=', startDate) .where('timeEntered', '<=', endDate) .get()]; case 1: - questionsSnapshot = _q.sent(); + questionsSnapshot = _j.sent(); userStats = {}; getWrappedUserDocs = function () { return __awaiter(void 0, void 0, void 0, function () { var docs; @@ -94,6 +94,41 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function } }); }); }; + getWrappedSessionDocs = function () { return __awaiter(void 0, void 0, void 0, function () { + var docs, sessionIds; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + docs = {}; + sessionIds = []; + questionsSnapshot.docs.map(function (doc) { return sessionIds.push(doc.get('sessionId')); }); + sessionIds.sort(); + // eslint-disable-next-line no-console + // console.log(sessionIds); + return [4 /*yield*/, Promise.all(sessionIds.map(function (id) { return __awaiter(void 0, void 0, void 0, function () { + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!id) return [3 /*break*/, 2]; + _a = docs; + _b = id; + return [4 /*yield*/, sessionsRef.doc(id).get()]; + case 1: + _a[_b] = _c.sent(); + _c.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }))]; + case 1: + // eslint-disable-next-line no-console + // console.log(sessionIds); + _a.sent(); + return [2 /*return*/, docs]; + } + }); + }); }; taCounts = {}; monthTimeCounts = {}; officeHourSessions = {}; @@ -135,12 +170,14 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function } else { // Handle the case where the document does not exist - errorUsers.push({ user: userId, error: "No document found for this user, skipping update." }); + errorUsers.push({ user: userId, + error: "No document found for this user, skipping update." }); } } } else { - errorUsers.push({ user: userId, error: "User is not an active student/TA or doesn't have favorite TA." }); + errorUsers.push({ user: userId, + error: "User is not an active student/TA or doesn't have favorite TA." }); } } else { @@ -202,213 +239,194 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numStudentsHelped: 0 }; TAsessions[answererId] = []; + // eslint-disable-next-line no-console + console.log(answererId + " was a student but now theyre a TA"); } }; - _loop_1 = function (doc) { - var question, answererId, askerId, sessionId, timeEntered, timeAddressed, sessionDoc, timeHelping, taAmt, minutesSpent; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - question = doc.data(); - answererId = question.answererId, askerId = question.askerId, sessionId = question.sessionId, timeEntered = question.timeEntered, timeAddressed = question.timeAddressed; - initializeUser(answererId, askerId); - return [4 /*yield*/, sessionsRef.doc(sessionId).get()]; - case 1: - sessionDoc = _a.sent(); - if (TAsessions[answererId].find(function (TAsession) { return TAsession.session === sessionId; }) === undefined) { - /* Since TA was active during this session and this is the first - time encountering the session, we add it to their timeHelped */ - if (sessionDoc.exists && userStats[answererId].timeHelpingStudents !== undefined) { - timeHelping = (sessionDoc.get('endTime').toDate().getTime() - - sessionDoc.get('startTime').toDate().getTime()) / 60000; - // this should never be less than 0 (or 0, really) - if (timeHelping >= 0) { - userStats[answererId].timeHelpingStudents = - ((_e = userStats[answererId].timeHelpingStudents) !== null && _e !== void 0 ? _e : 0) + timeHelping; - } - } - } - officeHourSessions[askerId] = officeHourSessions[askerId] || []; - if (!officeHourSessions[askerId].includes(sessionId)) { - officeHourSessions[askerId].push(sessionId); - } - if (answererId && timeAddressed) { - (_f = TAsessions[answererId]) === null || _f === void 0 ? void 0 : _f.push({ - session: sessionId, - asker: askerId, - courseId: sessionDoc.get('courseId'), - day: timeAddressed.toDate().getDay() - }); - if (!((_g = taCounts[askerId]) === null || _g === void 0 ? void 0 : _g.has(answererId))) { - (_h = taCounts[askerId]) === null || _h === void 0 ? void 0 : _h.set(answererId, 1); + processStats = function () { + var _a, _b; + count = 0; + var _loop_2 = function (userId, stats) { + if (count > 0 && count % 100 === 0) { + // eslint-disable-next-line no-console + console.log(count + "/" + Object.entries(userStats).length + " users processed."); + } + stats.numVisits = officeHourSessions[userId].length; + stats.totalMinutes = Math.ceil(stats.totalMinutes); + if (stats.timeHelpingStudents !== undefined) { + stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + if (stats.numStudentsHelped !== undefined) { + stats.numStudentsHelped = TAsessions[userId].length; + } + } + // Personality type + var weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) + / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks + var averageSessionsPerWeek = stats.numVisits / weeksInRange; + if (averageSessionsPerWeek >= 2) { + stats.personalityType = 'Consistent'; + } + else if (averageSessionsPerWeek >= 0.5) { + stats.personalityType = 'Resourceful'; + } + else { + stats.personalityType = 'Independent'; + } + // Month user spent the most time in + stats.favMonth = (_a = monthTimeCounts[userId]) === null || _a === void 0 ? void 0 : _a.indexOf(Math.max.apply(Math, monthTimeCounts[userId])); + // Get the ids in the map that have the highest counts + if (taCounts[userId].size !== 0) { + stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prevEntry, nextEntry) { return prevEntry[1] < nextEntry[1] ? nextEntry : prevEntry; })[0]; + } + // eslint-disable-next-line no-console + // console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); + if (stats.favTaId) { + // only looking at the sessions from the favorite TA that match with sessions the user went to + var resSession = (_b = TAsessions[stats.favTaId]) === null || _b === void 0 ? void 0 : _b.filter(function (TAsession) { + return officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId; + }); + if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1) { + stats.favClass = resSession[0].courseId; + stats.favDay = resSession[0].day; + } + else if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) > 1) { + /* filtering from general to specific: + - find mode class + - out of all the sessions for that class, find mode day + */ + // eslint-disable-next-line no-console + // console.log(`finding ${userId}'s mode stats`); + // // eslint-disable-next-line no-console + // console.log(stats.favTaId + "'s total sessions"); + // // eslint-disable-next-line no-console + // console.log(TAsessions[stats.favTaId]); + // // eslint-disable-next-line no-console + // console.log(stats.favTaId + `'s total sessions with ${userId}`); + // // eslint-disable-next-line no-console + // resSession.map((x) => console.log(x)); + var classFrequency_1 = {}; + var dayFrequency_1 = {}; + resSession.forEach(function (TAsession) { + if (!classFrequency_1[TAsession.courseId]) { + classFrequency_1[TAsession.courseId] = 1; } - else if (answererId && ((_j = taCounts[askerId]) === null || _j === void 0 ? void 0 : _j.has(answererId))) { - taAmt = (_k = taCounts[askerId]) === null || _k === void 0 ? void 0 : _k.get(answererId); - taAmt && ((_l = taCounts[askerId]) === null || _l === void 0 ? void 0 : _l.set(answererId, taAmt + 1)); + else { + classFrequency_1[TAsession.courseId] += 1; } - } - // Minutes spent at office hours - if (timeEntered) { - if (timeAddressed) { - minutesSpent = (timeAddressed.toDate().getTime() - - timeEntered.toDate().getTime()) / 60000; - if (minutesSpent >= 0) { - userStats[askerId].totalMinutes += minutesSpent; + }); + var modeCourseId_1 = Object.keys(classFrequency_1).reduce(function (courseId1, courseId2) { + return classFrequency_1[courseId1] > classFrequency_1[courseId2] ? courseId1 : courseId2; + }); + resSession.forEach(function (TAsession) { + if (TAsession.courseId === modeCourseId_1) { + if (!dayFrequency_1[TAsession.day]) { + dayFrequency_1[TAsession.day] = 1; + } + else { + dayFrequency_1[TAsession.day] += 1; } - monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += minutesSpent; - } - else { - userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed - monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } - // eslint-disable-next-line no-console - console.log("month counts: [" + monthTimeCounts[askerId] + "]"); - } - return [2 /*return*/]; + }); + var modeDay_1 = Object.keys(dayFrequency_1).reduce(function (day1, day2) { + return dayFrequency_1[parseInt(day1, 10)] > dayFrequency_1[parseInt(day2, 10)] ? day1 : day2; + }); + // eslint-disable-next-line no-console + // console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); + var modeSessions = resSession.filter(function (TAsession) { return TAsession.courseId === modeCourseId_1 + && TAsession.day === parseInt(modeDay_1, 10); }); + // eslint-disable-next-line no-console + // console.log("final filtering for modeSessions"); + // eslint-disable-next-line no-console + // console.log(modeSessions); + // There could be multiple ties, so just picking the first one + stats.favClass = modeSessions[0].courseId; + stats.favDay = modeSessions[0].day; + // eslint-disable-next-line no-console + // console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); + // eslint-disable-next-line no-console + // console.log("------------"); + } } - }); + count++; + }; + for (var _i = 0, _c = Object.entries(userStats); _i < _c.length; _i++) { + var _d = _c[_i], userId = _d[0], stats = _d[1]; + _loop_2(userId, stats); + } }; - _i = 0, _a = questionsSnapshot.docs; - _q.label = 2; + return [4 /*yield*/, getWrappedSessionDocs()]; case 2: - if (!(_i < _a.length)) return [3 /*break*/, 5]; - doc = _a[_i]; - return [5 /*yield**/, _loop_1(doc)]; - case 3: - _q.sent(); - _q.label = 4; - case 4: - _i++; - return [3 /*break*/, 2]; - case 5: - _loop_2 = function (userId, stats) { - // eslint-disable-next-line no-console - console.log("name is " + userId); - stats.numVisits = (_m = officeHourSessions[userId]) === null || _m === void 0 ? void 0 : _m.length; - stats.totalMinutes = Math.ceil(stats.totalMinutes); - if (stats.timeHelpingStudents !== undefined) { - stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); - if (stats.numStudentsHelped !== undefined) { - stats.numStudentsHelped = TAsessions[userId].length; - } - } - // Personality type - var weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) - / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks - var averageSessionsPerWeek = stats.numVisits / weeksInRange; - if (averageSessionsPerWeek >= 2) { - stats.personalityType = 'Consistent'; - } - else if (averageSessionsPerWeek >= 0.5) { - stats.personalityType = 'Resourceful'; + sessionDocs = _j.sent(); + count = 0; + _loop_1 = function (doc) { + // Console statement for debugging + if (count > 0 && count % 100 === 0) { + // eslint-disable-next-line no-console + console.log(count + "/" + questionsSnapshot.docs.length + " questions processed."); } - else { - stats.personalityType = 'Independent'; + var question = doc.data(); + var answererId = question.answererId, askerId = question.askerId, sessionId = question.sessionId, timeEntered = question.timeEntered, timeAddressed = question.timeAddressed; + initializeUser(answererId, askerId); + // Office hour visits + var sessionDoc = sessionDocs[sessionId]; + if (TAsessions[answererId].find(function (TAsession) { return TAsession.session === sessionId; }) === undefined) { + /* Since TA was active during this session and this is the first + time encountering the session, we add it to their timeHelped */ + if (sessionDoc.exists && userStats[answererId].timeHelpingStudents !== undefined) { + /* Add a total session time to the min TA helped */ + var timeHelping = (sessionDoc.get('endTime').toDate().getTime() + - sessionDoc.get('startTime').toDate().getTime()) / 60000; + // this should never be less than 0 (or 0, really) + if (timeHelping >= 0) { + userStats[answererId].timeHelpingStudents = + ((_b = userStats[answererId].timeHelpingStudents) !== null && _b !== void 0 ? _b : 0) + timeHelping; + } + } } - // Month user spent the most time in - stats.favMonth = (_o = monthTimeCounts[userId]) === null || _o === void 0 ? void 0 : _o.indexOf(Math.max.apply(Math, monthTimeCounts[userId])); - // Get the ids in the map that have the highest counts - if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prev, next) { return prev[1] < next[1] ? next : prev; })[0]; + officeHourSessions[askerId] = officeHourSessions[askerId] || []; + if (!officeHourSessions[askerId].includes(sessionId)) { + officeHourSessions[askerId].push(sessionId); } - // eslint-disable-next-line no-console - console.log(userId + "'s fav ta is " + stats.favTaId + ", taCounts length is " + taCounts[userId].size); - if (stats.favTaId) { - // eslint-disable-next-line no-console - console.log("here"); - // only looking at the sessions from the favorite TA that match with sessions the user went to - var resSession = (_p = TAsessions[stats.favTaId]) === null || _p === void 0 ? void 0 : _p.filter(function (TAsession) { - return officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId; + if (answererId && timeAddressed) { + (_c = TAsessions[answererId]) === null || _c === void 0 ? void 0 : _c.push({ + session: sessionId, + asker: askerId, + courseId: sessionDoc.get('courseId'), + day: timeAddressed.toDate().getDay() }); - if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) === 1) { - stats.favClass = resSession[0].courseId; - stats.favDay = resSession[0].day; - // eslint-disable-next-line no-console - console.log("finding " + userId + "'s mode stats"); - // eslint-disable-next-line no-console - console.log(stats.favTaId + "'s total sessions"); - // eslint-disable-next-line no-console - console.log(TAsessions[stats.favTaId]); - // eslint-disable-next-line no-console - console.log(stats.favTaId + ("'s total sessions with " + userId)); - // eslint-disable-next-line no-console - resSession.map(function (x) { return console.log(x); }); + if (!((_d = taCounts[askerId]) === null || _d === void 0 ? void 0 : _d.has(answererId))) { + (_e = taCounts[askerId]) === null || _e === void 0 ? void 0 : _e.set(answererId, 1); } - else if ((resSession === null || resSession === void 0 ? void 0 : resSession.length) > 1) { - /* filtering from general to specific: - - find mode class - - out of all the sessions for that class, find mode day - */ - // eslint-disable-next-line no-console - console.log("finding " + userId + "'s mode stats"); - // eslint-disable-next-line no-console - console.log(stats.favTaId + "'s total sessions"); - // eslint-disable-next-line no-console - console.log(TAsessions[stats.favTaId]); - // eslint-disable-next-line no-console - console.log(stats.favTaId + ("'s total sessions with " + userId)); - // eslint-disable-next-line no-console - resSession.map(function (x) { return console.log(x); }); - var classFrequency_1 = {}; - var dayFrequency_1 = {}; - /* - You need to find modeDay AFTER filtering all of modeCourse - because of the case where if you filter by looking at what - sessions have modeCourse AND modeDay, the modeDay might be a - number that doesn't exist with modeCourse. - */ - resSession.forEach(function (TAsession) { - if (!classFrequency_1[TAsession.courseId]) { - classFrequency_1[TAsession.courseId] = 1; - } - else { - classFrequency_1[TAsession.courseId] += 1; - } - }); - var modeCourseId_1 = Object.keys(classFrequency_1).reduce(function (a, b) { - return classFrequency_1[a] > classFrequency_1[b] ? a : b; - }); - resSession.forEach(function (TAsession) { - if (TAsession.courseId === modeCourseId_1) { - if (!dayFrequency_1[TAsession.day]) { - dayFrequency_1[TAsession.day] = 1; - } - else { - dayFrequency_1[TAsession.day] += 1; - } - } - }); - var modeDay_1 = Object.keys(dayFrequency_1).reduce(function (day1, day2) { - return dayFrequency_1[parseInt(day1, 10)] > dayFrequency_1[parseInt(day2, 10)] ? day1 : day2; - }); - // eslint-disable-next-line no-console - console.log("modeCourse: " + modeCourseId_1 + " and modeDay: " + modeDay_1); - var modeSessions = resSession.filter(function (TAsession) { return TAsession.courseId === modeCourseId_1 - && TAsession.day === parseInt(modeDay_1, 10); }); - // eslint-disable-next-line no-console - console.log("final filtering for modeSessions"); - // eslint-disable-next-line no-console - console.log(modeSessions); - // there could be multiple ties, so just picking the first one - stats.favClass = modeSessions[0].courseId; - stats.favDay = modeSessions[0].day; - // eslint-disable-next-line no-console - console.log("favClass: " + stats.favClass + ", favDay: " + stats.favDay); - // eslint-disable-next-line no-console - console.log("------------"); + else if (answererId && ((_f = taCounts[askerId]) === null || _f === void 0 ? void 0 : _f.has(answererId))) { + var taAmt = (_g = taCounts[askerId]) === null || _g === void 0 ? void 0 : _g.get(answererId); + taAmt && ((_h = taCounts[askerId]) === null || _h === void 0 ? void 0 : _h.set(answererId, taAmt + 1)); } } + // Minutes spent at office hours + if (timeEntered) { + if (timeAddressed) { + var minutesSpent = (timeAddressed.toDate().getTime() - + timeEntered.toDate().getTime()) / 60000; // convert ms to minutes + if (minutesSpent >= 0) { + userStats[askerId].totalMinutes += minutesSpent; + } + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += minutesSpent; + } + else { + userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed + monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; + } + } + count++; }; - // Personality type will be calculated after processing all documents - // Process stats - for (_b = 0, _c = Object.entries(userStats); _b < _c.length; _b++) { - _d = _c[_b], userId = _d[0], stats = _d[1]; - _loop_2(userId, stats); + for (_i = 0, _a = questionsSnapshot.docs; _i < _a.length; _i++) { + doc = _a[_i]; + _loop_1(doc); } + processStats(); return [4 /*yield*/, updateWrappedDocs()]; - case 6: - _q.sent(); - // debugging console log + case 3: + _j.sent(); // eslint-disable-next-line no-console errorUsers.forEach(function (errUser) { return console.log(errUser.user + ": " + errUser.error); }); return [2 /*return*/]; diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index cfceab655..c67275723 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -25,10 +25,10 @@ const endDate = admin.firestore.Timestamp.fromDate(new Date('2024-11-22')); const getWrapped = async () => { // Refs - const questionsRef = db.collection('questions-test'); - const sessionsRef = db.collection('sessions-test'); + const questionsRef = db.collection('questions'); + const sessionsRef = db.collection('sessions'); const wrappedRef = db.collection('wrapped-fa24'); - const usersRef = db.collection('users-test'); + const usersRef = db.collection('users'); // Query all questions asked between FA23 and SP24 const questionsSnapshot = await questionsRef @@ -59,6 +59,22 @@ const getWrapped = async () => { return docs; } + const getWrappedSessionDocs = async () => { + const docs: {[userId:string]: FirebaseFirestore.DocumentSnapshot} = {}; + const sessionIds: string[] = []; + questionsSnapshot.docs.map((doc) => sessionIds.push(doc.get('sessionId'))); + sessionIds.sort(); + // eslint-disable-next-line no-console + // console.log(sessionIds); + await Promise.all(sessionIds.map(async (id) => { + if (id) { + docs[id] = await sessionsRef.doc(id).get(); + } + } + )); + return docs; + } + const taCounts: {[userId: string] : Map} = {}; const monthTimeCounts: {[userId: string]: number[]} = {}; @@ -83,12 +99,12 @@ const getWrapped = async () => { If a user is only a student, they need to have at least one OH visit. If a user is a TA, they need to have at least one TA session AND at least one OH visit as a student. */ - const hasVisits = stats.numVisits > 0; // This is true if the user is either a student, or a TA who has more than one session const isUserActive = stats.timeHelpingStudents === undefined || (TAsessions[userId]?.length > 0); const hasFavoriteTa = stats.favTaId !== ""; - const taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) + const taStatsMismatched = + (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); if (hasVisits && isUserActive && hasFavoriteTa) { if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { @@ -108,11 +124,13 @@ const getWrapped = async () => { }); } else { // Handle the case where the document does not exist - errorUsers.push({user: userId, error: "No document found for this user, skipping update."}); + errorUsers.push({user: userId, + error: "No document found for this user, skipping update."}); } } } else { - errorUsers.push({user: userId, error: "User is not an active student/TA or doesn't have favorite TA."}); + errorUsers.push({user: userId, + error: "User is not an active student/TA or doesn't have favorite TA."}); } } else { errorUsers.push({user: userId, error: "User ID is undefined, skipping update."}); @@ -171,13 +189,133 @@ const getWrapped = async () => { numStudentsHelped: 0 }; TAsessions[answererId] = []; + // eslint-disable-next-line no-console + console.log(`${answererId} was a student but now theyre a TA`) } } - // do all sorting for preprocessing here + + const processStats = () => { + count = 0; + for (const [userId, stats] of Object.entries(userStats)) { + if (count > 0 && count % 100 === 0) { + // eslint-disable-next-line no-console + console.log(`${count}/${Object.entries(userStats).length} users processed.`); + } + stats.numVisits = officeHourSessions[userId].length; + stats.totalMinutes = Math.ceil(stats.totalMinutes); + if (stats.timeHelpingStudents !== undefined) { + stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); + if (stats.numStudentsHelped !== undefined) { + stats.numStudentsHelped = TAsessions[userId].length; + } + } + // Personality type + const weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) + / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks + const averageSessionsPerWeek = stats.numVisits / weeksInRange; + + if (averageSessionsPerWeek >= 2) { + stats.personalityType = 'Consistent'; + } else if (averageSessionsPerWeek >= 0.5) { + stats.personalityType = 'Resourceful'; + } else { + stats.personalityType = 'Independent'; + } + // Month user spent the most time in + stats.favMonth = monthTimeCounts[userId]?.indexOf(Math.max(...monthTimeCounts[userId])); + // Get the ids in the map that have the highest counts + if (taCounts[userId].size !== 0) { + stats.favTaId = Array.from(taCounts[userId].entries()).reduce( + (prevEntry, nextEntry) => prevEntry[1] < nextEntry[1] ? nextEntry : prevEntry)[0]; + } + // eslint-disable-next-line no-console + // console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); + if (stats.favTaId) { + // only looking at the sessions from the favorite TA that match with sessions the user went to + const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => + officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId); + if (resSession?.length === 1) { + stats.favClass = resSession[0].courseId; + stats.favDay = resSession[0].day; + } else if (resSession?.length > 1) { + /* filtering from general to specific: + - find mode class + - out of all the sessions for that class, find mode day + */ + + // eslint-disable-next-line no-console + // console.log(`finding ${userId}'s mode stats`); + // // eslint-disable-next-line no-console + // console.log(stats.favTaId + "'s total sessions"); + // // eslint-disable-next-line no-console + // console.log(TAsessions[stats.favTaId]); + // // eslint-disable-next-line no-console + // console.log(stats.favTaId + `'s total sessions with ${userId}`); + // // eslint-disable-next-line no-console + // resSession.map((x) => console.log(x)); + + const classFrequency: { [courseId: string]: number } = {}; + const dayFrequency: { [day: number]: number } = {}; + + resSession.forEach((TAsession) => { + if (!classFrequency[TAsession.courseId]) { + classFrequency[TAsession.courseId] = 1; + } else { + classFrequency[TAsession.courseId] += 1; + } + }); + + const modeCourseId = Object.keys(classFrequency).reduce((courseId1, courseId2) => + classFrequency[courseId1] > classFrequency[courseId2] ? courseId1 : courseId2); + resSession.forEach((TAsession) => { + if (TAsession.courseId === modeCourseId) { + if (!dayFrequency[TAsession.day]) { + dayFrequency[TAsession.day] = 1; + } else { + dayFrequency[TAsession.day] += 1; + } + } + }); + const modeDay = Object.keys(dayFrequency).reduce((day1, day2) => + dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2); + // eslint-disable-next-line no-console + // console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); + const modeSessions = resSession.filter((TAsession) => TAsession.courseId === modeCourseId + && TAsession.day === parseInt(modeDay,10)); + + // eslint-disable-next-line no-console + // console.log("final filtering for modeSessions"); + // eslint-disable-next-line no-console + // console.log(modeSessions); + + // There could be multiple ties, so just picking the first one + stats.favClass = modeSessions[0].courseId; + stats.favDay = modeSessions[0].day; + // eslint-disable-next-line no-console + // console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); + + // eslint-disable-next-line no-console + // console.log("------------"); + } + } + count++; + } + } + + // ----- end of helper functions -------- + + + const sessionDocs = await getWrappedSessionDocs(); + let count = 0; for (const doc of questionsSnapshot.docs) { + // Console statement for debugging + if (count > 0 && count % 100 === 0) { + // eslint-disable-next-line no-console + console.log(`${count}/${questionsSnapshot.docs.length} questions processed.`) + } const question = doc.data() as { answererId: string; askerId: string; @@ -189,15 +327,11 @@ const getWrapped = async () => { const { answererId, askerId, sessionId, timeEntered, timeAddressed } = question; initializeUser(answererId, askerId); - // Office hour visits - // TRYING TO FIX BELOW LINE - // eslint-disable-next-line no-await-in-loop - const sessionDoc = await sessionsRef.doc(sessionId).get(); + const sessionDoc = sessionDocs[sessionId]; if (TAsessions[answererId].find((TAsession) => TAsession.session === sessionId) === undefined) { /* Since TA was active during this session and this is the first time encountering the session, we add it to their timeHelped */ - if (sessionDoc.exists && userStats[answererId].timeHelpingStudents !== undefined ) { /* Add a total session time to the min TA helped */ const timeHelping = (sessionDoc.get('endTime').toDate().getTime() @@ -243,146 +377,16 @@ const getWrapped = async () => { userStats[askerId].totalMinutes += 60; // assume 60 minutes if not addressed monthTimeCounts[askerId][timeEntered.toDate().getMonth()] += 60; } - // eslint-disable-next-line no-console - console.log("month counts: [" + monthTimeCounts[askerId] + "]"); } + count++; } - // Personality type will be calculated after processing all documents - - - // Process stats - for (const [userId, stats] of Object.entries(userStats)) { - // eslint-disable-next-line no-console - console.log(`name is ${userId}`); - stats.numVisits = officeHourSessions[userId]?.length; - stats.totalMinutes = Math.ceil(stats.totalMinutes); - if (stats.timeHelpingStudents !== undefined) { - stats.timeHelpingStudents = Math.ceil(stats.timeHelpingStudents); - if (stats.numStudentsHelped !== undefined) { - stats.numStudentsHelped = TAsessions[userId].length; - } - } - // Personality type - const weeksInRange = (endDate.toDate().getTime() - startDate.toDate().getTime()) - / (1000 * 60 * 60 * 24 * 7); // convert ms to weeks - const averageSessionsPerWeek = stats.numVisits / weeksInRange; - - if (averageSessionsPerWeek >= 2) { - stats.personalityType = 'Consistent'; - } else if (averageSessionsPerWeek >= 0.5) { - stats.personalityType = 'Resourceful'; - } else { - stats.personalityType = 'Independent'; - } - - // Month user spent the most time in - stats.favMonth = monthTimeCounts[userId]?.indexOf(Math.max(...monthTimeCounts[userId])); - // Get the ids in the map that have the highest counts - if (taCounts[userId].size !== 0) { - stats.favTaId = Array.from(taCounts[userId].entries()).reduce( - (prev, next) => prev[1] < next[1] ? next : prev)[0]; - } - // eslint-disable-next-line no-console - console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); - if (stats.favTaId) { - // eslint-disable-next-line no-console - console.log(`here`); - // only looking at the sessions from the favorite TA that match with sessions the user went to - const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => - officeHourSessions[userId].includes(TAsession.session) && TAsession.asker === userId); - if (resSession?.length === 1) { - stats.favClass = resSession[0].courseId; - stats.favDay = resSession[0].day; - // eslint-disable-next-line no-console - console.log(`finding ${userId}'s mode stats`); - // eslint-disable-next-line no-console - console.log(stats.favTaId + "'s total sessions"); - // eslint-disable-next-line no-console - console.log(TAsessions[stats.favTaId]); - // eslint-disable-next-line no-console - console.log(stats.favTaId + `'s total sessions with ${userId}`); - // eslint-disable-next-line no-console - resSession.map((x) => console.log(x)); - } else if (resSession?.length > 1) { - /* filtering from general to specific: - - find mode class - - out of all the sessions for that class, find mode day - */ - - // eslint-disable-next-line no-console - console.log(`finding ${userId}'s mode stats`); - // eslint-disable-next-line no-console - console.log(stats.favTaId + "'s total sessions"); - // eslint-disable-next-line no-console - console.log(TAsessions[stats.favTaId]); - // eslint-disable-next-line no-console - console.log(stats.favTaId + `'s total sessions with ${userId}`); - // eslint-disable-next-line no-console - resSession.map((x) => console.log(x)); - - - const classFrequency: { [courseId: string]: number } = {}; - const dayFrequency: { [day: number]: number } = {}; - - /* - You need to find modeDay AFTER filtering all of modeCourse - because of the case where if you filter by looking at what - sessions have modeCourse AND modeDay, the modeDay might be a - number that doesn't exist with modeCourse. - */ - - resSession.forEach((TAsession) => { - if (!classFrequency[TAsession.courseId]) { - classFrequency[TAsession.courseId] = 1; - } else { - classFrequency[TAsession.courseId] += 1; - } - }); - - - const modeCourseId = Object.keys(classFrequency).reduce((a, b) => - classFrequency[a] > classFrequency[b] ? a : b); - resSession.forEach((TAsession) => { - if (TAsession.courseId === modeCourseId) { - if (!dayFrequency[TAsession.day]) { - dayFrequency[TAsession.day] = 1; - } else { - dayFrequency[TAsession.day] += 1; - } - } - }); - const modeDay = Object.keys(dayFrequency).reduce((day1, day2) => - dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2); - // eslint-disable-next-line no-console - console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); - const modeSessions = resSession.filter((TAsession) => TAsession.courseId === modeCourseId - && TAsession.day === parseInt(modeDay,10)); - - // eslint-disable-next-line no-console - console.log("final filtering for modeSessions"); - // eslint-disable-next-line no-console - console.log(modeSessions); - // there could be multiple ties, so just picking the first one - stats.favClass = modeSessions[0].courseId; - stats.favDay = modeSessions[0].day; - // eslint-disable-next-line no-console - console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); - - // eslint-disable-next-line no-console - console.log("------------"); - - - } - } - } + processStats(); await updateWrappedDocs(); - // debugging console log + // eslint-disable-next-line no-console errorUsers.forEach((errUser) => console.log(errUser.user + ": " + errUser.error)); - - } (async () => { From bcd841effa04e9276fea0662b9335aae070a7089 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Thu, 14 Nov 2024 22:44:01 -0500 Subject: [PATCH 06/16] added extra check for tas so no 0 amounts show up --- src/scripts/wrapped-fa24.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index c67275723..d58d46780 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -102,11 +102,14 @@ const getWrapped = async () => { const hasVisits = stats.numVisits > 0; // This is true if the user is either a student, or a TA who has more than one session const isUserActive = stats.timeHelpingStudents === undefined || (TAsessions[userId]?.length > 0); + // True if user is either a student, or TA who helped more than 0 students for more than 0 minutes + const taHelped = stats.timeHelpingStudents === undefined || + (stats.numStudentsHelped && stats.timeHelpingStudents > 0 && stats.numStudentsHelped > 0); const hasFavoriteTa = stats.favTaId !== ""; const taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); - if (hasVisits && isUserActive && hasFavoriteTa) { + if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { errorUsers.push({user: userId, error: `User is active and has favorite TA but missing one of the following: From 6070b5bd5b47d31ca8ff4ecb06cc63ed8bf451a6 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Fri, 15 Nov 2024 11:37:05 -0500 Subject: [PATCH 07/16] pushing edited js file --- src/scripts/wrapped-fa24.js | 6 ++++-- src/scripts/wrapped-fa24.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index bcc55c514..fb46bc88e 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -134,7 +134,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function officeHourSessions = {}; TAsessions = {}; updateWrappedDocs = function () { return __awaiter(void 0, void 0, void 0, function () { - var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, hasFavoriteTa, taStatsMismatched, wrappedDocRef, userDoc; + var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, taHelped, hasFavoriteTa, taStatsMismatched, wrappedDocRef, userDoc; var _c; return __generator(this, function (_d) { switch (_d.label) { @@ -149,10 +149,12 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function if (userId) { hasVisits = stats.numVisits > 0; isUserActive = stats.timeHelpingStudents === undefined || (((_c = TAsessions[userId]) === null || _c === void 0 ? void 0 : _c.length) > 0); + taHelped = stats.timeHelpingStudents === undefined || + (stats.numStudentsHelped && stats.timeHelpingStudents > 0 && stats.numStudentsHelped > 0); hasFavoriteTa = stats.favTaId !== ""; taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); - if (hasVisits && isUserActive && hasFavoriteTa) { + if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { errorUsers.push({ user: userId, error: "User is active and has favorite TA but missing one of the following:\n favClass: " + stats.favClass + ", favDay: " + stats.favDay + ", favMonth: " + stats.favMonth }); } diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index d58d46780..31ea88506 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -60,7 +60,7 @@ const getWrapped = async () => { } const getWrappedSessionDocs = async () => { - const docs: {[userId:string]: FirebaseFirestore.DocumentSnapshot} = {}; + const docs: {[sessionId:string]: FirebaseFirestore.DocumentSnapshot} = {}; const sessionIds: string[] = []; questionsSnapshot.docs.map((doc) => sessionIds.push(doc.get('sessionId'))); sessionIds.sort(); From ba06555edc0136c48ae4de5b9a5f9c4e77da9c48 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Mon, 18 Nov 2024 10:33:43 -0500 Subject: [PATCH 08/16] undoing main changes and deleting console logs --- .github/workflows/cd.yml | 2 +- README.md | 2 +- src/scripts/wrapped-fa24.ts | 33 +++------------------------------ 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ed53373c3..a3f6ed013 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,7 +2,7 @@ name: CD on: push: branches: - - main + - master jobs: deploy: diff --git a/README.md b/README.md index 17589ee8a..da546060e 100644 --- a/README.md +++ b/README.md @@ -117,5 +117,5 @@ _Screenshots showing major parts of app_ We are a team within **Cornell Design & Tech Initiative**. For more information, see our [website](https://cornelldti.org/). - + ​ diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index 31ea88506..d69c7107b 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -9,8 +9,6 @@ admin.initializeApp({ // eslint-disable-next-line no-console console.log('Firebase admin initialized!'); - - // Initialize Firestore const db = admin.firestore(); const errorUsers: { @@ -75,7 +73,6 @@ const getWrapped = async () => { return docs; } - const taCounts: {[userId: string] : Map} = {}; const monthTimeCounts: {[userId: string]: number[]} = {}; const officeHourSessions: { [userId: string]: string[]} = {}; @@ -192,8 +189,6 @@ const getWrapped = async () => { numStudentsHelped: 0 }; TAsessions[answererId] = []; - // eslint-disable-next-line no-console - console.log(`${answererId} was a student but now theyre a TA`) } } @@ -234,8 +229,7 @@ const getWrapped = async () => { stats.favTaId = Array.from(taCounts[userId].entries()).reduce( (prevEntry, nextEntry) => prevEntry[1] < nextEntry[1] ? nextEntry : prevEntry)[0]; } - // eslint-disable-next-line no-console - // console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); + if (stats.favTaId) { // only looking at the sessions from the favorite TA that match with sessions the user went to const resSession = TAsessions[stats.favTaId]?.filter( (TAsession) => @@ -249,17 +243,6 @@ const getWrapped = async () => { - out of all the sessions for that class, find mode day */ - // eslint-disable-next-line no-console - // console.log(`finding ${userId}'s mode stats`); - // // eslint-disable-next-line no-console - // console.log(stats.favTaId + "'s total sessions"); - // // eslint-disable-next-line no-console - // console.log(TAsessions[stats.favTaId]); - // // eslint-disable-next-line no-console - // console.log(stats.favTaId + `'s total sessions with ${userId}`); - // // eslint-disable-next-line no-console - // resSession.map((x) => console.log(x)); - const classFrequency: { [courseId: string]: number } = {}; const dayFrequency: { [day: number]: number } = {}; @@ -284,24 +267,14 @@ const getWrapped = async () => { }); const modeDay = Object.keys(dayFrequency).reduce((day1, day2) => dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2); - // eslint-disable-next-line no-console - // console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); + const modeSessions = resSession.filter((TAsession) => TAsession.courseId === modeCourseId && TAsession.day === parseInt(modeDay,10)); - - // eslint-disable-next-line no-console - // console.log("final filtering for modeSessions"); - // eslint-disable-next-line no-console - // console.log(modeSessions); // There could be multiple ties, so just picking the first one stats.favClass = modeSessions[0].courseId; stats.favDay = modeSessions[0].day; - // eslint-disable-next-line no-console - // console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); - - // eslint-disable-next-line no-console - // console.log("------------"); + } } count++; From 1a7d672858cf7492697aa017ab0fc0db6675a75e Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Tue, 19 Nov 2024 22:28:45 -0500 Subject: [PATCH 09/16] took out fav stats constraints --- src/scripts/wrapped-fa24.js | 33 +-------------------------------- src/scripts/wrapped-fa24.ts | 8 +------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index fb46bc88e..0346af2cd 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -103,8 +103,6 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function sessionIds = []; questionsSnapshot.docs.map(function (doc) { return sessionIds.push(doc.get('sessionId')); }); sessionIds.sort(); - // eslint-disable-next-line no-console - // console.log(sessionIds); return [4 /*yield*/, Promise.all(sessionIds.map(function (id) { return __awaiter(void 0, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) { @@ -122,8 +120,6 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function }); }); }))]; case 1: - // eslint-disable-next-line no-console - // console.log(sessionIds); _a.sent(); return [2 /*return*/, docs]; } @@ -155,10 +151,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { - if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { - errorUsers.push({ user: userId, error: "User is active and has favorite TA but missing one of the following:\n favClass: " + stats.favClass + ", favDay: " + stats.favDay + ", favMonth: " + stats.favMonth }); - } - else if (taStatsMismatched) { + if (taStatsMismatched) { errorUsers.push({ user: userId, error: "Mismatch in updating ta specfic values." }); } else { @@ -241,8 +234,6 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function numStudentsHelped: 0 }; TAsessions[answererId] = []; - // eslint-disable-next-line no-console - console.log(answererId + " was a student but now theyre a TA"); } }; processStats = function () { @@ -280,8 +271,6 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function if (taCounts[userId].size !== 0) { stats.favTaId = Array.from(taCounts[userId].entries()).reduce(function (prevEntry, nextEntry) { return prevEntry[1] < nextEntry[1] ? nextEntry : prevEntry; })[0]; } - // eslint-disable-next-line no-console - // console.log(`${userId}'s fav ta is ${stats.favTaId}, taCounts length is ${taCounts[userId].size}`); if (stats.favTaId) { // only looking at the sessions from the favorite TA that match with sessions the user went to var resSession = (_b = TAsessions[stats.favTaId]) === null || _b === void 0 ? void 0 : _b.filter(function (TAsession) { @@ -296,16 +285,6 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function - find mode class - out of all the sessions for that class, find mode day */ - // eslint-disable-next-line no-console - // console.log(`finding ${userId}'s mode stats`); - // // eslint-disable-next-line no-console - // console.log(stats.favTaId + "'s total sessions"); - // // eslint-disable-next-line no-console - // console.log(TAsessions[stats.favTaId]); - // // eslint-disable-next-line no-console - // console.log(stats.favTaId + `'s total sessions with ${userId}`); - // // eslint-disable-next-line no-console - // resSession.map((x) => console.log(x)); var classFrequency_1 = {}; var dayFrequency_1 = {}; resSession.forEach(function (TAsession) { @@ -332,21 +311,11 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function var modeDay_1 = Object.keys(dayFrequency_1).reduce(function (day1, day2) { return dayFrequency_1[parseInt(day1, 10)] > dayFrequency_1[parseInt(day2, 10)] ? day1 : day2; }); - // eslint-disable-next-line no-console - // console.log(`modeCourse: ${modeCourseId} and modeDay: ${modeDay}`); var modeSessions = resSession.filter(function (TAsession) { return TAsession.courseId === modeCourseId_1 && TAsession.day === parseInt(modeDay_1, 10); }); - // eslint-disable-next-line no-console - // console.log("final filtering for modeSessions"); - // eslint-disable-next-line no-console - // console.log(modeSessions); // There could be multiple ties, so just picking the first one stats.favClass = modeSessions[0].courseId; stats.favDay = modeSessions[0].day; - // eslint-disable-next-line no-console - // console.log(`favClass: ${stats.favClass}, favDay: ${stats.favDay}`); - // eslint-disable-next-line no-console - // console.log("------------"); } } count++; diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index d69c7107b..7aaf8997b 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -62,8 +62,6 @@ const getWrapped = async () => { const sessionIds: string[] = []; questionsSnapshot.docs.map((doc) => sessionIds.push(doc.get('sessionId'))); sessionIds.sort(); - // eslint-disable-next-line no-console - // console.log(sessionIds); await Promise.all(sessionIds.map(async (id) => { if (id) { docs[id] = await sessionsRef.doc(id).get(); @@ -107,11 +105,7 @@ const getWrapped = async () => { (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { - if (!(stats.favClass && stats.favDay !== -1 && stats.favMonth !== -1)) { - errorUsers.push({user: userId, - error: `User is active and has favorite TA but missing one of the following: - favClass: ${stats.favClass}, favDay: ${stats.favDay}, favMonth: ${stats.favMonth}`}); - } else if (taStatsMismatched) { + if (taStatsMismatched) { errorUsers.push({user: userId, error: "Mismatch in updating ta specfic values."}) } else { const wrappedDocRef = wrappedRef.doc(userId); From 997af906c5ddb72ae1850b15ed8cac84bd88b5f8 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Tue, 19 Nov 2024 22:31:47 -0500 Subject: [PATCH 10/16] added initial values to reduce and extra check for undefined course --- src/scripts/wrapped-fa24.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index 7aaf8997b..6014feb60 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -248,8 +248,8 @@ const getWrapped = async () => { } }); - const modeCourseId = Object.keys(classFrequency).reduce((courseId1, courseId2) => - classFrequency[courseId1] > classFrequency[courseId2] ? courseId1 : courseId2); + const modeCourseId = Object.keys(classFrequency).reduce(((courseId1:string, courseId2:string) => + classFrequency[courseId1] > classFrequency[courseId2] ? courseId1 : courseId2), ""); resSession.forEach((TAsession) => { if (TAsession.courseId === modeCourseId) { if (!dayFrequency[TAsession.day]) { @@ -259,8 +259,8 @@ const getWrapped = async () => { } } }); - const modeDay = Object.keys(dayFrequency).reduce((day1, day2) => - dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2); + const modeDay = Object.keys(dayFrequency).reduce(((day1, day2) => + dayFrequency[parseInt(day1, 10)] > dayFrequency[parseInt(day2,10)] ? day1 : day2), ""); const modeSessions = resSession.filter((TAsession) => TAsession.courseId === modeCourseId && TAsession.day === parseInt(modeDay,10)); @@ -317,12 +317,13 @@ const getWrapped = async () => { officeHourSessions[askerId] = officeHourSessions[askerId] || []; if (!officeHourSessions[askerId].includes(sessionId)) { officeHourSessions[askerId].push(sessionId); } - - if (answererId && timeAddressed) { + + const course = sessionDoc.get('courseId'); + if (answererId && timeAddressed && course) { TAsessions[answererId]?.push({ session: sessionId, asker: askerId, - courseId: sessionDoc.get('courseId'), + courseId: course, day: timeAddressed.toDate().getDay() }); From d6623b8123903c646e0c363e08d9e2c930f6c7a9 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Wed, 20 Nov 2024 00:13:03 -0500 Subject: [PATCH 11/16] pushing js file --- src/scripts/wrapped-fa24.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index 0346af2cd..642cc8357 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -295,9 +295,9 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function classFrequency_1[TAsession.courseId] += 1; } }); - var modeCourseId_1 = Object.keys(classFrequency_1).reduce(function (courseId1, courseId2) { + var modeCourseId_1 = Object.keys(classFrequency_1).reduce((function (courseId1, courseId2) { return classFrequency_1[courseId1] > classFrequency_1[courseId2] ? courseId1 : courseId2; - }); + }), ""); resSession.forEach(function (TAsession) { if (TAsession.courseId === modeCourseId_1) { if (!dayFrequency_1[TAsession.day]) { @@ -308,9 +308,9 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function } } }); - var modeDay_1 = Object.keys(dayFrequency_1).reduce(function (day1, day2) { + var modeDay_1 = Object.keys(dayFrequency_1).reduce((function (day1, day2) { return dayFrequency_1[parseInt(day1, 10)] > dayFrequency_1[parseInt(day2, 10)] ? day1 : day2; - }); + }), ""); var modeSessions = resSession.filter(function (TAsession) { return TAsession.courseId === modeCourseId_1 && TAsession.day === parseInt(modeDay_1, 10); }); // There could be multiple ties, so just picking the first one @@ -358,11 +358,12 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function if (!officeHourSessions[askerId].includes(sessionId)) { officeHourSessions[askerId].push(sessionId); } - if (answererId && timeAddressed) { + var course = sessionDoc.get('courseId'); + if (answererId && timeAddressed && course) { (_c = TAsessions[answererId]) === null || _c === void 0 ? void 0 : _c.push({ session: sessionId, asker: askerId, - courseId: sessionDoc.get('courseId'), + courseId: course, day: timeAddressed.toDate().getDay() }); if (!((_d = taCounts[askerId]) === null || _d === void 0 ? void 0 : _d.has(answererId))) { From c014d383907a08ea55916de51b8fcf5bc7ce9288 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Wed, 20 Nov 2024 13:17:36 -0500 Subject: [PATCH 12/16] readding check for month and removing hasFavTa --- src/scripts/wrapped-fa24.js | 6 +++--- src/scripts/wrapped-fa24.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scripts/wrapped-fa24.js b/src/scripts/wrapped-fa24.js index 642cc8357..0b9033e47 100644 --- a/src/scripts/wrapped-fa24.js +++ b/src/scripts/wrapped-fa24.js @@ -130,7 +130,7 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function officeHourSessions = {}; TAsessions = {}; updateWrappedDocs = function () { return __awaiter(void 0, void 0, void 0, function () { - var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, taHelped, hasFavoriteTa, taStatsMismatched, wrappedDocRef, userDoc; + var batch, userDocuments, _i, _a, _b, userId, stats, hasVisits, isUserActive, taHelped, hasMinutes, taStatsMismatched, wrappedDocRef, userDoc; var _c; return __generator(this, function (_d) { switch (_d.label) { @@ -147,10 +147,10 @@ var getWrapped = function () { return __awaiter(void 0, void 0, void 0, function isUserActive = stats.timeHelpingStudents === undefined || (((_c = TAsessions[userId]) === null || _c === void 0 ? void 0 : _c.length) > 0); taHelped = stats.timeHelpingStudents === undefined || (stats.numStudentsHelped && stats.timeHelpingStudents > 0 && stats.numStudentsHelped > 0); - hasFavoriteTa = stats.favTaId !== ""; + hasMinutes = stats.favMonth !== -1 && stats.totalMinutes > 0; taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); - if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { + if (hasVisits && isUserActive && hasMinutes && taHelped) { if (taStatsMismatched) { errorUsers.push({ user: userId, error: "Mismatch in updating ta specfic values." }); } diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index 6014feb60..e9ff82771 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -100,11 +100,11 @@ const getWrapped = async () => { // True if user is either a student, or TA who helped more than 0 students for more than 0 minutes const taHelped = stats.timeHelpingStudents === undefined || (stats.numStudentsHelped && stats.timeHelpingStudents > 0 && stats.numStudentsHelped > 0); - const hasFavoriteTa = stats.favTaId !== ""; + const hasMinutes = stats.favMonth !== -1 && stats.totalMinutes > 0; const taStatsMismatched = (stats.timeHelpingStudents !== undefined && stats.numStudentsHelped === undefined) || (stats.timeHelpingStudents === undefined && stats.numStudentsHelped !== undefined); - if (hasVisits && isUserActive && hasFavoriteTa && taHelped) { + if (hasVisits && isUserActive && hasMinutes && taHelped) { if (taStatsMismatched) { errorUsers.push({user: userId, error: "Mismatch in updating ta specfic values."}) } else { From 01ffc3a5d802482fe07f9b28051c0cdf8b212db2 Mon Sep 17 00:00:00 2001 From: rgu0114 Date: Thu, 21 Nov 2024 15:33:10 -0500 Subject: [PATCH 13/16] deploy run changes --- src/scripts/wrapped-fa24.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/wrapped-fa24.ts b/src/scripts/wrapped-fa24.ts index e9ff82771..5fd7c220c 100644 --- a/src/scripts/wrapped-fa24.ts +++ b/src/scripts/wrapped-fa24.ts @@ -2,7 +2,7 @@ import admin from "firebase-admin"; admin.initializeApp({ credential: admin.credential.applicationDefault(), - databaseURL: 'https://qmi-test.firebaseio.com' + databaseURL: 'https://queue-me-in-prod.firebaseio.com' }); @@ -25,7 +25,7 @@ const getWrapped = async () => { // Refs const questionsRef = db.collection('questions'); const sessionsRef = db.collection('sessions'); - const wrappedRef = db.collection('wrapped-fa24'); + const wrappedRef = db.collection('wrapped'); const usersRef = db.collection('users'); // Query all questions asked between FA23 and SP24 From fed45dc520b0fd55e8cd4fa75746cd2aa51f3ab9 Mon Sep 17 00:00:00 2001 From: rgu0114 Date: Thu, 21 Nov 2024 15:33:28 -0500 Subject: [PATCH 14/16] wrapped validation script --- src/scripts/query-wrapped.ts | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/scripts/query-wrapped.ts diff --git a/src/scripts/query-wrapped.ts b/src/scripts/query-wrapped.ts new file mode 100644 index 000000000..013a1f61e --- /dev/null +++ b/src/scripts/query-wrapped.ts @@ -0,0 +1,58 @@ +import admin from 'firebase-admin'; + +// Initialize Firebase Admin with credentials and database URL +admin.initializeApp({ + credential: admin.credential.applicationDefault(), + databaseURL: 'https://queue-me-in-prod.firebaseio.com' +}); + +// Initialize Firestore database +const db = admin.firestore(); + +async function validateDocuments() { + // Reference to the 'wrapped' collection + const wrappedRef = db.collection('wrapped'); + + try { + // Get all documents in the collection + const snapshot = await wrappedRef.get(); + + // Print the total number of documents + // eslint-disable-next-line no-console + console.log(`Total number of documents in 'wrapped-fa24': ${snapshot.size}`); + + // Iterate over each document + snapshot.forEach(doc => { + const data = doc.data(); + const errors = []; + + // Check if numVisits is a positive number + if (data.numVisits <= 0) { + errors.push('numVisits is not positive'); + } + + // Check if personalityType is not an empty string + if (!data.personalityType || data.personalityType.trim() === '') { + errors.push('personalityType is empty'); + } + + // Check if totalMinutes is a positive number + if (data.totalMinutes <= 0) { + errors.push('totalMinutes is not positive'); + } + + // If there are errors, log them with the user ID + if (errors.length > 0) { + // eslint-disable-next-line no-console + console.log(`Document ID: ${doc.id} has the following issues: ${errors.join(', ')}`); + } + }); + } catch (error) { + // Log any errors that occur during the fetch + // eslint-disable-next-line no-console + console.error('Error retrieving documents:', error); + } +} + +// Call the function to execute it +validateDocuments(); From 0aa0aa79ad44965ac8f33005be571d30f3dbc43f Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Fri, 22 Nov 2024 00:24:25 -0500 Subject: [PATCH 15/16] extra check that all students have a taId --- src/scripts/query-wrapped.js | 102 +++++++++++++++++++++++++++++++++++ src/scripts/query-wrapped.ts | 5 ++ 2 files changed, 107 insertions(+) create mode 100644 src/scripts/query-wrapped.js diff --git a/src/scripts/query-wrapped.js b/src/scripts/query-wrapped.js new file mode 100644 index 000000000..7672c9f15 --- /dev/null +++ b/src/scripts/query-wrapped.js @@ -0,0 +1,102 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +var firebase_admin_1 = require("firebase-admin"); +// Initialize Firebase Admin with credentials and database URL +firebase_admin_1["default"].initializeApp({ + credential: firebase_admin_1["default"].credential.applicationDefault(), + databaseURL: 'https://queue-me-in-prod.firebaseio.com' +}); +// Initialize Firestore database +var db = firebase_admin_1["default"].firestore(); +function validateDocuments() { + return __awaiter(this, void 0, void 0, function () { + var wrappedRef, snapshot, error_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + wrappedRef = db.collection('wrapped'); + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, wrappedRef.get()]; + case 2: + snapshot = _a.sent(); + // Print the total number of documents + // eslint-disable-next-line no-console + console.log("Total number of documents in 'wrapped-fa24': " + snapshot.size); + // Iterate over each document + snapshot.forEach(function (doc) { + var data = doc.data(); + var errors = []; + // Check if numVisits is a positive number + if (data.numVisits <= 0) { + errors.push('numVisits is not positive'); + } + // Check if personalityType is not an empty string + if (!data.personalityType || data.personalityType.trim() === '') { + errors.push('personalityType is empty'); + } + // Check if totalMinutes is a positive number + if (data.totalMinutes <= 0) { + errors.push('totalMinutes is not positive'); + } + // Check if favTa exists for students + if (!data.timeHelpingStudents && data.numStudentsHelped === 0 && data.favTaId) { + errors.push('favTaId is missing'); + } + // If there are errors, log them with the user ID + if (errors.length > 0) { + // eslint-disable-next-line no-console + console.log("Document ID: " + doc.id + " has the following issues: " + errors.join(', ')); + } + }); + return [3 /*break*/, 4]; + case 3: + error_1 = _a.sent(); + // Log any errors that occur during the fetch + // eslint-disable-next-line no-console + console.error('Error retrieving documents:', error_1); + return [3 /*break*/, 4]; + case 4: return [2 /*return*/]; + } + }); + }); +} +// Call the function to execute it +validateDocuments(); diff --git a/src/scripts/query-wrapped.ts b/src/scripts/query-wrapped.ts index 013a1f61e..d1e856ee9 100644 --- a/src/scripts/query-wrapped.ts +++ b/src/scripts/query-wrapped.ts @@ -41,6 +41,11 @@ async function validateDocuments() { errors.push('totalMinutes is not positive'); } + // Check if favTa exists for students + if (!data.timeHelpingStudents && data.numStudentsHelped === 0 && data.favTaId) { + errors.push('favTaId is missing'); + } + // If there are errors, log them with the user ID if (errors.length > 0) { // eslint-disable-next-line no-console From dfa3807403ed59355922fc338bb209067d8fe6c8 Mon Sep 17 00:00:00 2001 From: NIDHI2023 Date: Thu, 5 Dec 2024 00:09:13 -0500 Subject: [PATCH 16/16] added extra checks for fields in verification script --- src/scripts/query-wrapped.js | 19 ++++++++++++++++++- src/scripts/query-wrapped.ts | 23 ++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/scripts/query-wrapped.js b/src/scripts/query-wrapped.js index 7672c9f15..7d219e48d 100644 --- a/src/scripts/query-wrapped.js +++ b/src/scripts/query-wrapped.js @@ -77,9 +77,26 @@ function validateDocuments() { errors.push('totalMinutes is not positive'); } // Check if favTa exists for students - if (!data.timeHelpingStudents && data.numStudentsHelped === 0 && data.favTaId) { + if (!data.timeHelpingStudents && !data.numStudentsHelped && !data.favTaId) { errors.push('favTaId is missing'); } + // Check if one of the TA stats exists but the other doesn't + if ((data.timeHelpingStudents && !data.numStudentsHelped) + || (!data.timeHelpingStudents && data.numStudentsHelped)) { + errors.push('One of the TA stats is mismatched'); + } + // Check if favClass is not an empty string + if (!data.favClass || data.favClass.trim() === '') { + errors.push('favClass is empty'); + } + // Check if favDay is not a default value + if (data.favDay === -1) { + errors.push('favDay is not valid'); + } + // Check if favMonth is not a default value + if (data.favMonth === -1) { + errors.push('favMonth is not valid'); + } // If there are errors, log them with the user ID if (errors.length > 0) { // eslint-disable-next-line no-console diff --git a/src/scripts/query-wrapped.ts b/src/scripts/query-wrapped.ts index d1e856ee9..5da693877 100644 --- a/src/scripts/query-wrapped.ts +++ b/src/scripts/query-wrapped.ts @@ -42,10 +42,31 @@ async function validateDocuments() { } // Check if favTa exists for students - if (!data.timeHelpingStudents && data.numStudentsHelped === 0 && data.favTaId) { + if (!data.timeHelpingStudents && !data.numStudentsHelped && !data.favTaId) { errors.push('favTaId is missing'); } + // Check if one of the TA stats exists but the other doesn't + if ((data.timeHelpingStudents && !data.numStudentsHelped) + || (!data.timeHelpingStudents && data.numStudentsHelped)) { + errors.push('One of the TA stats is mismatched') + } + + // Check if favClass is not an empty string + if (!data.favClass || data.favClass.trim() === '') { + errors.push('favClass is empty'); + } + + // Check if favDay is not a default value + if (data.favDay === -1) { + errors.push('favDay is not valid'); + } + + // Check if favMonth is not a default value + if (data.favMonth === -1) { + errors.push('favMonth is not valid'); + } + // If there are errors, log them with the user ID if (errors.length > 0) { // eslint-disable-next-line no-console