@@ -8,6 +8,8 @@ import { userProfileRoutes } from './user-profile/profile-routes'; | |||||
import { categoryRoutes } from './user-profile/category-routes'; | import { categoryRoutes } from './user-profile/category-routes'; | ||||
import { shelfRoutes } from './user-profile/shelf-routes'; | import { shelfRoutes } from './user-profile/shelf-routes'; | ||||
import { libraryRoutes } from './library/library-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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'; | 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('/category/', categoryRoutes); | ||||
app.use('/shelf/', shelfRoutes); | app.use('/shelf/', shelfRoutes); | ||||
app.use('/library/', libraryRoutes); | app.use('/library/', libraryRoutes); | ||||
app.use('/recollection/', recollectionRoutes); | |||||
app.use('/recollection-history/', recollectionHistoryRoutes); | |||||
app.get('/', (request, response) => { | app.get('/', (request, response) => { | ||||
response.send('Server running @ port' + app.get('port')); | 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 { viewPermissionType } from "./variables"; | ||||
import { Word } from "./word"; | import { Word } from "./word"; | ||||
interface RevisionHistoryRecord { | |||||
day: Date, | |||||
wasRecallSuccessful: boolean | |||||
} | |||||
interface AddedWord { | |||||
interface ShelfWord { | |||||
word: Word, | word: Word, | ||||
notes: Array<string>, | notes: Array<string>, | ||||
isFavourite: boolean, | |||||
nextRevisionDateTime: Date, | nextRevisionDateTime: Date, | ||||
spaceBetweenRecall: number, // in Days | spaceBetweenRecall: number, // in Days | ||||
revisionHistory : Array<RevisionHistoryRecord>, | |||||
isArchived: boolean, | isArchived: boolean, | ||||
} | } | ||||
@@ -21,13 +14,13 @@ export interface Shelf { | |||||
description?: string, | description?: string, | ||||
viewType: viewPermissionType, | viewType: viewPermissionType, | ||||
isArchived: boolean, | isArchived: boolean, | ||||
addedWords?: Array<AddedWord> | |||||
words?: Array<ShelfWord> | |||||
} | } | ||||
export interface MongoAddedWord extends Omit<AddedWord, "word"> { | |||||
export interface MongoShelfWord extends Omit<ShelfWord, "word"> { | |||||
word: string, | 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 { Category } from "./category"; | ||||
import { Shelf } from "./shelf"; | import { Shelf } from "./shelf"; | ||||
import { Word } from "./word"; | |||||
export interface User { | export interface User { | ||||
_id: string, | _id: string, | ||||
@@ -10,9 +11,11 @@ export interface User { | |||||
otp: number, | otp: number, | ||||
categories?: Array<Category>, | categories?: Array<Category>, | ||||
uncategorised?: Shelf, | 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, | 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 express from 'express'; | ||||
import passport from 'passport'; | import passport from 'passport'; | ||||
import { MongoShelf } from '../models/shelf'; | |||||
import { DB_NAME, getDatabaseClient } from '../db-utils'; | import { DB_NAME, getDatabaseClient } from '../db-utils'; | ||||
import { MongoCategory } from '../models/category'; | |||||
import { MongoUser } from '../models/user'; | import { MongoUser } from '../models/user'; | ||||
import { ObjectId } from 'bson'; | |||||
import { Word } from '../models/word'; | import { Word } from '../models/word'; | ||||
export const userProfileRoutes = express.Router(); | export const userProfileRoutes = express.Router(); | ||||
@@ -19,91 +17,86 @@ userProfileRoutes.get('/profile/', jwtAuthentication, async (request, response) | |||||
email: user.email, | email: user.email, | ||||
isVerified: user.isVerified, | isVerified: user.isVerified, | ||||
categories: user.categories, | categories: user.categories, | ||||
uncategorised: user.uncategorised | |||||
uncategorised: user.uncategorised, | |||||
favouriteWords: user.favouriteWords, | |||||
}); | }); | ||||
return; | 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 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'); | 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; | 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 express from 'express'; | ||||
import passport from 'passport'; | import passport from 'passport'; | ||||
import { MongoCategory } from '../models/category'; | import { MongoCategory } from '../models/category'; | ||||
import { DB_NAME } from '../db-utils'; | import { DB_NAME } from '../db-utils'; | ||||
import { getDatabaseClient } 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'; | import { Word } from '../models/word'; | ||||
export const shelfRoutes = express.Router(); | export const shelfRoutes = express.Router(); | ||||
@@ -99,7 +99,7 @@ shelfRoutes.post('/add/', jwtAuthentication, async (request, response) => { | |||||
shelfRoutes.put('/update/', jwtAuthentication, async (request, response) => { | shelfRoutes.put('/update/', jwtAuthentication, async (request, response) => { | ||||
const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves'); | const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('shelves'); | ||||
const wordCollection = getDatabaseClient().db(DB_NAME).collection<Word>('words'); | 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({ | const currentShelf = await shelfCollection.findOne({ | ||||
_id: new ObjectId(request.body._id) | _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) { | for (let i = 0; i < updatedAddedWords.length; i += 1) { | ||||
if (!updatedAddedWords[i].word || updatedAddedWords[i].isArchived === undefined || | 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) { | !updatedAddedWords[i].spaceBetweenRecall) { | ||||
response.status(400); | response.status(400); | ||||
response.send("Missing key in " + JSON.stringify(updatedAddedWords[i])); | response.send("Missing key in " + JSON.stringify(updatedAddedWords[i])); | ||||