| @@ -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')); | |||
| @@ -0,0 +1,6 @@ | |||
| export interface RecollectionHistory { | |||
| userId: string, | |||
| wordId: string, | |||
| timeStamp: Date, | |||
| wasRecallSuccessful: boolean | |||
| } | |||
| @@ -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<string>, | |||
| isFavourite: boolean, | |||
| nextRevisionDateTime: Date, | |||
| spaceBetweenRecall: number, // in Days | |||
| revisionHistory : Array<RevisionHistoryRecord>, | |||
| isArchived: boolean, | |||
| } | |||
| @@ -21,13 +14,13 @@ export interface Shelf { | |||
| description?: string, | |||
| viewType: viewPermissionType, | |||
| isArchived: boolean, | |||
| addedWords?: Array<AddedWord> | |||
| words?: Array<ShelfWord> | |||
| } | |||
| export interface MongoAddedWord extends Omit<AddedWord, "word"> { | |||
| export interface MongoShelfWord extends Omit<ShelfWord, "word"> { | |||
| word: string, | |||
| } | |||
| export interface MongoShelf extends Omit<Shelf, "addedWords"> { | |||
| addedWords?: Array<MongoAddedWord> | |||
| export interface MongoShelf extends Omit<Shelf, "words"> { | |||
| words?: Array<MongoShelfWord> | |||
| } | |||
| @@ -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<Category>, | |||
| uncategorised?: Shelf, | |||
| favouriteWords?: Array<Word>, | |||
| } | |||
| export interface MongoUser extends Omit<Omit<User, "categories">, "uncategorised"> { | |||
| export interface MongoUser extends Omit<User, "categories" | "uncategorised" | "favouriteWords"> { | |||
| categories?: Array<string> // Category IDs, | |||
| uncategorised: string | |||
| uncategorised?: string, // Shelf ID | |||
| favouriteWords?: Array<string> // Favourite Words ID | |||
| } | |||
| @@ -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<MongoShelf>('shelves'); | |||
| const categoryCollection = getDatabaseClient().db(DB_NAME).collection<MongoCategory>('categories'); | |||
| }); | |||
| @@ -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<MongoCategory>('categories'); | |||
| const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves'); | |||
| const userCollection = getDatabaseClient().db(DB_NAME).collection<MongoUser>('users'); | |||
| const wordCollection = getDatabaseClient().db(DB_NAME).collection<Word>('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<MongoUser>('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; | |||
| }); | |||
| @@ -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<RecollectionHistory>('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<RecollectionHistory>('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; | |||
| } | |||
| }); | |||
| @@ -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<MongoShelf>('shelves'); | |||
| const wordCollection = getDatabaseClient().db(DB_NAME).collection<Word>('words'); | |||
| const updatedAddedWords: Array<MongoAddedWord> = request.body.addedWords ? request.body.addedWords : []; | |||
| const updatedAddedWords: Array<MongoShelfWord> = 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])); | |||