From dfc8077be1c85b5f5384eb7eedd19288c382966d Mon Sep 17 00:00:00 2001 From: kj1352 Date: Wed, 20 Oct 2021 17:45:28 +0530 Subject: [PATCH] Added APIs for recollection history and favourites --- src/index.ts | 4 + src/models/recollection-history.ts | 6 + src/models/shelf.ts | 17 +-- src/models/user.ts | 7 +- src/revision/revision-routes.ts | 16 +++ src/user-profile/profile-routes.ts | 129 +++++++++--------- .../recollection-history-routes.ts | 59 ++++++++ src/user-profile/shelf-routes.ts | 9 +- 8 files changed, 160 insertions(+), 87 deletions(-) create mode 100644 src/models/recollection-history.ts create mode 100644 src/revision/revision-routes.ts create mode 100644 src/user-profile/recollection-history-routes.ts diff --git a/src/index.ts b/src/index.ts index 50f5547..3658528 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,8 @@ import { userProfileRoutes } from './user-profile/profile-routes'; import { categoryRoutes } from './user-profile/category-routes'; import { shelfRoutes } from './user-profile/shelf-routes'; import { libraryRoutes } from './library/library-routes'; +import { recollectionRoutes } from './revision/revision-routes'; +import { recollectionHistoryRoutes } from './user-profile/recollection-history-routes'; const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY || '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'; @@ -23,6 +25,8 @@ app.use('/user/', userProfileRoutes); app.use('/category/', categoryRoutes); app.use('/shelf/', shelfRoutes); app.use('/library/', libraryRoutes); +app.use('/recollection/', recollectionRoutes); +app.use('/recollection-history/', recollectionHistoryRoutes); app.get('/', (request, response) => { response.send('Server running @ port' + app.get('port')); diff --git a/src/models/recollection-history.ts b/src/models/recollection-history.ts new file mode 100644 index 0000000..90b7615 --- /dev/null +++ b/src/models/recollection-history.ts @@ -0,0 +1,6 @@ +export interface RecollectionHistory { + userId: string, + wordId: string, + timeStamp: Date, + wasRecallSuccessful: boolean +} \ No newline at end of file diff --git a/src/models/shelf.ts b/src/models/shelf.ts index 06a9396..6522dcc 100644 --- a/src/models/shelf.ts +++ b/src/models/shelf.ts @@ -1,18 +1,11 @@ import { viewPermissionType } from "./variables"; import { Word } from "./word"; -interface RevisionHistoryRecord { - day: Date, - wasRecallSuccessful: boolean -} - -interface AddedWord { +interface ShelfWord { word: Word, notes: Array, - isFavourite: boolean, nextRevisionDateTime: Date, spaceBetweenRecall: number, // in Days - revisionHistory : Array, isArchived: boolean, } @@ -21,13 +14,13 @@ export interface Shelf { description?: string, viewType: viewPermissionType, isArchived: boolean, - addedWords?: Array + words?: Array } -export interface MongoAddedWord extends Omit { +export interface MongoShelfWord extends Omit { word: string, } -export interface MongoShelf extends Omit { - addedWords?: Array +export interface MongoShelf extends Omit { + words?: Array } diff --git a/src/models/user.ts b/src/models/user.ts index 72c9b2a..f213c6b 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,5 +1,6 @@ import { Category } from "./category"; import { Shelf } from "./shelf"; +import { Word } from "./word"; export interface User { _id: string, @@ -10,9 +11,11 @@ export interface User { otp: number, categories?: Array, uncategorised?: Shelf, + favouriteWords?: Array, } -export interface MongoUser extends Omit, "uncategorised"> { +export interface MongoUser extends Omit { categories?: Array // Category IDs, - uncategorised: string + uncategorised?: string, // Shelf ID + favouriteWords?: Array // Favourite Words ID } \ No newline at end of file diff --git a/src/revision/revision-routes.ts b/src/revision/revision-routes.ts new file mode 100644 index 0000000..ffd632a --- /dev/null +++ b/src/revision/revision-routes.ts @@ -0,0 +1,16 @@ +import express from 'express'; +import passport from 'passport'; +import { MongoShelf } from '../models/shelf'; +import { DB_NAME, getDatabaseClient } from '../db-utils'; +import { MongoCategory } from '../models/category'; + +export const recollectionRoutes = express.Router(); + +export const jwtAuthentication = passport.authenticate('jwt', { session: false }); + +recollectionRoutes.get('/questions/', jwtAuthentication, async (request, response) => { + const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); + const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); + + +}); \ No newline at end of file diff --git a/src/user-profile/profile-routes.ts b/src/user-profile/profile-routes.ts index 703f78c..48652b4 100644 --- a/src/user-profile/profile-routes.ts +++ b/src/user-profile/profile-routes.ts @@ -1,10 +1,8 @@ -import { ObjectId } from 'bson'; import express from 'express'; import passport from 'passport'; -import { MongoShelf } from '../models/shelf'; import { DB_NAME, getDatabaseClient } from '../db-utils'; -import { MongoCategory } from '../models/category'; import { MongoUser } from '../models/user'; +import { ObjectId } from 'bson'; import { Word } from '../models/word'; export const userProfileRoutes = express.Router(); @@ -19,91 +17,86 @@ userProfileRoutes.get('/profile/', jwtAuthentication, async (request, response) email: user.email, isVerified: user.isVerified, categories: user.categories, - uncategorised: user.uncategorised + uncategorised: user.uncategorised, + favouriteWords: user.favouriteWords, }); return; }); +// Add favourite word -userProfileRoutes.get('/profile/deep-copy/', jwtAuthentication, async (request, response) => { +userProfileRoutes.post('/favourite-words/', jwtAuthentication, async (request, response) => { const user: MongoUser = (request.user as any); - const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); - const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); + const userCollection = getDatabaseClient().db(DB_NAME).collection('users'); const wordCollection = getDatabaseClient().db(DB_NAME).collection('words'); - let deepCategories = []; + if (!user.favouriteWords) { + user.favouriteWords = []; + } - for (let i = 0; i < user.categories.length; i += 1) { - let deepShelves = []; + if (!request.body.wordId) { + response.status(400); + response.send("Missing wordID"); + return; + } else if (user.favouriteWords.includes(request.body.wordId)) { + response.status(400); + response.send("WordID already present"); + return; + } - const matchedCategory: any = await categoryCollection.findOne({ - _id: new ObjectId(user.categories[i]) - }); + const matchedWord = await wordCollection.findOne({ + _id: new ObjectId(request.body.wordId) + }); - if (matchedCategory && matchedCategory.shelves) { - for (let j = 0; j < matchedCategory.shelves.length; j += 1) { - let deepAddedWords = []; - - const matchedShelf: any = await shelfCollection.findOne({ - _id: new ObjectId(matchedCategory.shelves[j]) - }); - - - if (matchedShelf && matchedShelf.addedWords) { - for (let k = 0; k < matchedShelf.addedWords.length; k += 1) { - const matchedWord = await wordCollection.findOne({ - _id: new ObjectId(matchedShelf.addedWords[k].word) - }); - - if (matchedWord) { - deepAddedWords.push(matchedWord); - } - } - } - - matchedShelf.addedWords = deepAddedWords; - - - deepShelves.push(matchedShelf); - } - } + if (!matchedWord) { + response.status(400); + response.send("Word not present in the library"); + return; + } - matchedCategory.shelves = deepShelves; - - deepCategories.push(matchedCategory); + user.favouriteWords.push(request.body.wordId); + const updatedUser = await userCollection.updateOne({ + _id: new ObjectId(user._id) + }, { + $set: { + favouriteWords: user.favouriteWords + } + }); + + if (updatedUser.acknowledged) { + response.sendStatus(200); + } else { + response.sendStatus(500); } - let deepUncategorisedAddedWords = []; - - const matchedUncategoisedShelf: any = await shelfCollection.findOne({ - _id: new ObjectId(user.uncategorised) - }); - - if (matchedUncategoisedShelf && matchedUncategoisedShelf.addedWords) { - for (let k = 0; k < matchedUncategoisedShelf.addedWords.length; k += 1) { - const matchedWord = await wordCollection.findOne({ - _id: new ObjectId(matchedUncategoisedShelf.addedWords[k].word) - }); - - if (matchedWord) { - deepUncategorisedAddedWords.push(matchedWord); - } - } + return; +}); - matchedUncategoisedShelf.addedWords = deepUncategorisedAddedWords; +// Update favourite word +userProfileRoutes.put('/favourite-words/', jwtAuthentication, async (request, response) => { + const user: MongoUser = (request.user as any); + const userCollection = getDatabaseClient().db(DB_NAME).collection('users'); + + if (!request.body.favouriteWords) { + response.status(400); + response.send("Missing favouriteWords param"); + return; } - response.json({ - _id: user._id, - name: user.name, - email: user.email, - isVerified: user.isVerified, - categories: deepCategories, - uncategorised: matchedUncategoisedShelf + const updatedUser = await userCollection.updateOne({ + _id: new ObjectId(user._id) + }, { + $set: { + favouriteWords: request.body.favouriteWords + } }); - + if (updatedUser.acknowledged) { + response.sendStatus(200); + } else { + response.sendStatus(500); + } return; }); \ No newline at end of file diff --git a/src/user-profile/recollection-history-routes.ts b/src/user-profile/recollection-history-routes.ts new file mode 100644 index 0000000..14e7221 --- /dev/null +++ b/src/user-profile/recollection-history-routes.ts @@ -0,0 +1,59 @@ +import { RecollectionHistory } from '../models/recollection-history'; +import express from 'express'; +import passport from 'passport'; +import { DB_NAME, getDatabaseClient } from '../db-utils'; +import { MongoUser } from '../models/user'; +import { ObjectId } from 'bson'; + +export const recollectionHistoryRoutes = express.Router(); + +export const jwtAuthentication = passport.authenticate('jwt', { session: false }); + + +// Get recollection history for the user +recollectionHistoryRoutes.get('', jwtAuthentication, async (request, response) => { + const user: MongoUser = (request.user as any); + const recollectionHistoryCollection = getDatabaseClient().db(DB_NAME).collection('recollection-history'); + + const recollectionHistoryRecords = await recollectionHistoryCollection.find({ + userId: new ObjectId(user._id) + }).toArray(); + + if (recollectionHistoryRecords.length > 0) { + response.status(200); + response.json(recollectionHistoryRecords); + } else { + response.status(200); + response.json([]); + } + + return; +}); + + +// Add user recollection history +recollectionHistoryRoutes.post('', jwtAuthentication, async (request, response) => { + const user: MongoUser = (request.user as any); + const recollectionHistoryCollection = getDatabaseClient().db(DB_NAME).collection('recollection-history'); + + if (!request.body.userId || !request.body.wordId || !request.body.timeStamp || !request.body.wasRecallSuccessful) { + response.status(400); + response.send("Missing attributes"); + return; + } + + const newRecollectionHistoryRecord = await recollectionHistoryCollection.insertOne({ + userId: user._id, + wordId: request.body.wordId, + timeStamp: request.body.timeStamp, + wasRecallSuccessful: request.body.wasRecallSuccessful + }); + + if (newRecollectionHistoryRecord.acknowledged) { + response.sendStatus(200); + return; + } else { + response.sendStatus(500); + return; + } +}); \ No newline at end of file diff --git a/src/user-profile/shelf-routes.ts b/src/user-profile/shelf-routes.ts index 856dbd2..ea3196b 100644 --- a/src/user-profile/shelf-routes.ts +++ b/src/user-profile/shelf-routes.ts @@ -1,10 +1,10 @@ -import { ObjectID, ObjectId } from 'bson'; +import { ObjectId } from 'bson'; import express from 'express'; import passport from 'passport'; import { MongoCategory } from '../models/category'; import { DB_NAME } from '../db-utils'; import { getDatabaseClient } from '../db-utils'; -import { MongoAddedWord, MongoShelf } from '../models/shelf'; +import { MongoShelfWord, MongoShelf } from '../models/shelf'; import { Word } from '../models/word'; export const shelfRoutes = express.Router(); @@ -99,7 +99,7 @@ shelfRoutes.post('/add/', jwtAuthentication, async (request, response) => { shelfRoutes.put('/update/', jwtAuthentication, async (request, response) => { const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); const wordCollection = getDatabaseClient().db(DB_NAME).collection('words'); - const updatedAddedWords: Array = request.body.addedWords ? request.body.addedWords : []; + const updatedAddedWords: Array = request.body.addedWords ? request.body.addedWords : []; const currentShelf = await shelfCollection.findOne({ _id: new ObjectId(request.body._id) @@ -121,8 +121,7 @@ shelfRoutes.put('/update/', jwtAuthentication, async (request, response) => { for (let i = 0; i < updatedAddedWords.length; i += 1) { if (!updatedAddedWords[i].word || updatedAddedWords[i].isArchived === undefined || - updatedAddedWords[i].isFavourite === undefined || !updatedAddedWords[i].nextRevisionDateTime || - updatedAddedWords[i].notes === undefined || updatedAddedWords[i].revisionHistory === undefined || + !updatedAddedWords[i].nextRevisionDateTime || updatedAddedWords[i].notes === undefined || !updatedAddedWords[i].spaceBetweenRecall) { response.status(400); response.send("Missing key in " + JSON.stringify(updatedAddedWords[i]));