diff --git a/controller/quesController.js b/controller/quesController.js index 41d50d9..9bbc7ab 100644 --- a/controller/quesController.js +++ b/controller/quesController.js @@ -43,23 +43,25 @@ export const createQuestion = async (req, res) => { const imageUrls = await processImages(data.images); - + const options = await Promise.all(data.options.map(async (option) => { - + let optionImageUrls = []; if (option.image && Array.isArray(option.image) && option.image.length > 0) { optionImageUrls = await processImages(option.image); } return { - image: optionImageUrls, - optionDb: { name: option.name, - tag: option.isCorrect === true ? "Correct" : "Incorrect", - images: optionImageUrls.length > 0 ? optionImageUrls.map(image => ({ url: image?.getUrl, key: image?.key })) : null,} + image: optionImageUrls, + optionDb: { + name: option.name, + tag: option.isCorrect === true ? "Correct" : "Incorrect", + images: optionImageUrls.length > 0 ? optionImageUrls.map(image => ({ url: image?.getUrl, key: image?.key })) : null, + } }; })); const optionsSignedUrls = options.flatMap(option => (option.image ? option.image.map(image => image.putUrl) : [])); - + const hasCorrectOption = options.some( (option) => option.optionDb.tag === 'Correct' ); @@ -69,7 +71,7 @@ export const createQuestion = async (req, res) => { message: 'At least one option must be correct', }); } - + const newQuestion = new Ques({ question: data.question, options: options.map((option) => option.optionDb), @@ -117,7 +119,7 @@ export const deleteQuestion = async (req, res) => { .status(404) .json({ success: false, message: "User not found" }); - if(question.images && question.images.length > 0) await deleteImages(question.images); + if (question.images && question.images.length > 0) await deleteImages(question.images); question.options.forEach(async (option) => { if (option.images && option.images.length > 0) { @@ -139,31 +141,55 @@ export const deleteQuestion = async (req, res) => { } }; + export const getAllQuestion = async (req, res) => { try { const queryObject = {}; + // Handle standard, subject, and createdBy queries if (req.query.standard) queryObject.standard = req.query.standard; if (req.query.subject) queryObject.subject = req.query.subject; - if (req.query.chapter) queryObject.chapter = req.query.chapter; - if (req.query.topic) queryObject.topics = req.query.topic; if (req.query.createdBy) queryObject.createdBy = req.query.createdBy; + // Handle multiple chapters + if (req.query.chapter) { + const chapters = Array.isArray(req.query.chapter) + ? req.query.chapter + : req.query.chapter.split(',').map(ch => ch.trim()); + queryObject.chapter = { $in: chapters }; + } + + // Handle single or multiple topics + if (req.query.topic) { + const topics = Array.isArray(req.query.topic) + ? req.query.topic + : req.query.topic.split(',').map(tp => tp.trim()); + queryObject.topics = { $in: topics }; + } + + // Handle subtopics + if (req.query.subtopic) { + const subtopics = Array.isArray(req.query.subtopic) + ? req.query.subtopic + : req.query.subtopic.split(',').map(st => st.trim()); + queryObject.subtopics = { $in: subtopics } + } + + // Handle search query if (req.query.search) { const searchTerms = req.query.search.split(' ').filter(term => term !== ''); - const searchRegex = searchTerms.map(term => new RegExp(term, 'i')); - queryObject.$and = [ { $or: searchRegex.map(regex => ({ question: regex })) } ]; } - console.log(queryObject); + console.log(queryObject); // For debugging purposes + + let formattedQuestions = []; - let formattedQuestions = [] if (req.user.role === "admin") { - let questionsData = Ques.find(queryObject).sort({ createdAt: -1 }); + let questionsData = Ques.find(queryObject).sort({ createdAt: -1 }); let page = req.query.page || 1; let limit = req.query.limit || 50; @@ -180,7 +206,7 @@ export const getAllQuestion = async (req, res) => { formattedQuestions = questions.map(question => ({ ...question.toObject(), - nestedSubTopic: question.nestedSubTopic || "" + nestedSubTopic: question.nestedSubTopic || "" })); } @@ -251,6 +277,7 @@ export const getAllQuestion = async (req, res) => { }, }); } catch (error) { + console.error("Error in getAllQuestion:", error); return res.status(500).json({ success: false, message: error.message || "Internal Server Error", @@ -263,58 +290,82 @@ export const getTotalQuestions = async (req, res) => { const queryObject = {}; const userId = req.user._id; + // Handle standard and subject queries if (req.query.standard) queryObject.standard = req.query.standard; if (req.query.subject) queryObject.subject = req.query.subject; - if (req.query.chapter) queryObject.chapter = req.query.chapter; - if (req.query.topic) queryObject.topics = req.query.topic; - if (req.query.createdBy) queryObject.createdBy = req.query.createdBy; - // const myquestion = req.query.createdBy ? true : false; + // Handle multiple chapters + if (req.query.chapter) { + const chapters = Array.isArray(req.query.chapter) + ? req.query.chapter + : req.query.chapter.split(',').map(chapter => chapter.trim()); + queryObject.chapter = { $in: chapters }; + } + + // Handle single or multiple topics + if (req.query.topic) { + const topics = Array.isArray(req.query.topic) + ? req.query.topic + : req.query.topic.split(',').map(topic => topic.trim()); + queryObject.topics = { $in: topics }; + } + + // Handle subtopics + if (req.query.subtopic) { + const subtopics = Array.isArray(req.query.subtopic) + ? req.query.subtopic + : req.query.subtopic.split(',').map(subtopic => subtopic.trim()); + + queryObject.subtopics = { $in: subtopics }; + } + + // Handle createdBy query + if (req.query.createdBy) queryObject.createdBy = req.query.createdBy; + // Handle search query if (req.query.search) { const searchTerms = req.query.search.split(' ').filter(term => term !== ''); const searchRegex = searchTerms.map(term => new RegExp(term, 'i')); - queryObject.$and = [{ $or: searchRegex.map(regex => ({ question: regex })) }]; } + // Handle mySearch query to search within user's created questions if (req.query.mySearch) { const searchTerms = req.query.mySearch.split(' ').filter(term => term !== ''); const searchRegex = searchTerms.map(term => new RegExp(term, 'i')); - queryObject.$and = [ { $or: searchRegex.map(regex => ({ question: regex })) }, { createdBy: userId }, ]; } - console.log(queryObject); + console.log(queryObject); // For debugging purposes + // Get the total count of questions matching the query const totalQuestions = await Ques.countDocuments(queryObject); - let myQuestions, questionsLength, fixedTotalQuestions, totalMyQuestions, totalMyPages, totalPages; + // Get the count of questions created by the user + const myQuestionsQueryObject = { ...queryObject, createdBy: userId }; + const totalMyQuestions = await Ques.countDocuments(myQuestionsQueryObject); - // if (myquestion) { - const queryObjects = { ...queryObject, createdBy: userId }; - myQuestions = await Ques.find(queryObjects); - questionsLength = myQuestions.length; - totalMyQuestions = await Ques.countDocuments({ createdBy: userId }); - totalMyPages = Math.ceil(totalMyQuestions / req.query.questionsPerPage); - + // Calculate total pages + const questionsPerPage = parseInt(req.query.questionsPerPage) || 10; // Set a default value if not provided + const totalPages = Math.ceil(totalQuestions / questionsPerPage); + const totalMyPages = Math.ceil(totalMyQuestions / questionsPerPage); - // const totalMyQuestions = await Ques.countDocuments({ createdBy: userId }); - fixedTotalQuestions = await Ques.countDocuments({}); - totalPages = Math.ceil(totalQuestions / req.query.questionsPerPage); + // Get the total count of all questions in the collection + const fixedTotalQuestions = await Ques.countDocuments({}); return res.status(200).json({ success: true, totalQuestions: totalQuestions, - questionsLength: questionsLength, - // totalSearchQuestions: totalSearchQuestions, - fixedTotalQuestions: fixedTotalQuestions, totalMyQuestions: totalMyQuestions, + fixedTotalQuestions: fixedTotalQuestions, + totalPages: totalPages, + totalMyPages: totalMyPages, }); } catch (error) { + console.error("Error in getTotalQuestions:", error); // Added logging for debugging return res.status(500).json({ success: false, message: error.message || "Internal Server Error", @@ -324,15 +375,15 @@ export const getTotalQuestions = async (req, res) => { export const getMyQuestions = async (req, res) => { try { - const userId = req.user._id; + const userId = req.user._id; const queryObject = { createdBy: userId }; - + if (req.query.standard) queryObject.standard = req.query.standard; if (req.query.subject) queryObject.subject = req.query.subject; if (req.query.chapter) queryObject.chapter = req.query.chapter; if (req.query.topic) queryObject.topics = req.query.topic; - if (req.query.search) { + if (req.query.search) { // Split search query by spaces to handle multiple words const searchTerms = req.query.search.split(' ').filter(term => term !== ''); @@ -351,7 +402,7 @@ export const getMyQuestions = async (req, res) => { let page = req.query.page || 1; let limit = req.query.limit || 50; - + let skip = (page - 1) * limit; questionsData = questionsData.skip(skip).limit(limit); @@ -392,14 +443,14 @@ export const getMyQuestions = async (req, res) => { // Determine the rank of the current user const userRank = userCounts.findIndex(user => user.userId.toString() === userId.toString()) + 1; - res.status(200).json({ - success: true, + res.status(200).json({ + success: true, questions: paginatedQuestions, todaysQuestionsCount: todaysQuestions.length, userRank: userRank, totalQuestions: totalQuestions }); - + } catch (error) { console.error(error); res.status(500).json({ success: false, message: "Server error" }); @@ -474,13 +525,13 @@ export const updateOption = async (req, res) => { export const allUser = async (req, res) => { try { - const users = await User.find({}) - if(!users) return res.status(404).json({ error: "Users not found" }); + const users = await User.find({}) + if (!users) return res.status(404).json({ error: "Users not found" }); - res.status(200).json({ + res.status(200).json({ success: true, users - }); + }); } catch (error) { console.error('Error fetching questions:', error); res.status(500).json({ error: error.message }); @@ -543,3 +594,73 @@ export const updateQuestionDetails = async (req, res) => { } }; + + +export const updateQuestionTopics = async (req, res) => { + try { + const { questionIds, topic, selectedTopic } = req.body; + + // Ensure questionIds is an array + const ids = Array.isArray(questionIds) ? questionIds : [questionIds]; + + if (ids.length === 0) { + return res.status(400).json({ message: 'No question IDs provided.' }); + } + + const existingQuestions = await Ques.find({ _id: { $in: ids } }); + if (existingQuestions.length === 0) { + return res.status(404).json({ message: 'No questions found with the provided IDs.' }); + } + + const updateOperations = existingQuestions.map(async (question) => { + let updateFields = {}; + + // Ensure topics is an array + question.topics = question.topics || []; + + // Case 1: Add the new topic if no selected topic exists + if (topic && !selectedTopic) { + if (!question.topics.includes(topic.trim())) { + question.topics.push(topic.trim()); + updateFields.topics = question.topics; + } + } + + // Case 2: Replace the selected topic with the new one + if (topic && selectedTopic) { + const index = question.topics.indexOf(selectedTopic.trim()); + if (index !== -1) { + question.topics[index] = topic.trim(); + } else { + question.topics.push(topic.trim()); + } + updateFields.topics = question.topics; + } + + // Only update if there are changes + if (Object.keys(updateFields).length > 0) { + await Ques.updateOne({ _id: question._id }, { $set: updateFields }); + } + + return { questionId: question._id, updatedFields: updateFields }; + }); + + const results = await Promise.all(updateOperations); + + res.status(200).json({ message: 'Questions updated successfully.', updateOperations: results }); + } catch (error) { + console.error('Error updating questions:', error); + res.status(500).json({ message: 'An error occurred while updating questions.' }); + } +}; + + + + + + + + + + + diff --git a/controller/subtopicController.js b/controller/subtopicController.js index b3055ab..3f5df3a 100644 --- a/controller/subtopicController.js +++ b/controller/subtopicController.js @@ -107,6 +107,7 @@ export const getSubtopics = async (req, res) => { const chapterNameArray = chapterName.split(',').map(name => name.trim()); const topicNameArray = topicName.split(',').map(name => name.trim()); + // Fetch the subject and populate chapters, topics, and subtopics const subject = await Subject.findOne({ name: subjectName, standard, @@ -116,37 +117,38 @@ export const getSubtopics = async (req, res) => { populate: { path: 'topics', match: { name: { $in: topicNameArray } }, - populate: 'subtopics' + populate: { + path: 'subtopics', // Ensure this path includes subtopic names + select: 'name', // Select fields you need + } } }); if (!subject) { - return res.status(400).json({ success: false, message: "Subject not found" }); + return res.status(404).json({ success: false, message: "Subject not found" }); } let subtopics = []; + // Flatten and collect all subtopics from the populated data subject.chapters.forEach(chapter => { chapter.topics.forEach(topic => { subtopics.push(...topic.subtopics); }); }); - for (let i = 0; i < subtopics.length; i++) { - const subtopic = subtopics[i]; - if (subtopic.subtopics && subtopic.subtopics.length > 0) { - const nestedSubtopicIds = subtopic.subtopics; - const nestedSubtopics = await Subtopic.find({ _id: { $in: nestedSubtopicIds } }); - subtopics[i].subtopics = nestedSubtopics; - } - } + // Map subtopics with their names + subtopics = subtopics.map(subtopic => ({ + _id: subtopic._id, + name: subtopic.name, // Use the name directly + })); res.status(200).json({ success: true, subtopics, }); } catch (error) { - console.error('Error in getSubTopic:', error); + console.error('Error in getSubtopics:', error); res.status(500).json({ success: false, message: error.message || 'Internal Server Error', diff --git a/controller/topicController.js b/controller/topicController.js index 2f1b0fc..cb7924c 100644 --- a/controller/topicController.js +++ b/controller/topicController.js @@ -139,16 +139,25 @@ export const getTopic = async (req, res) => { let filter = {}; - if (subjectName) filter.subjectName = subjectName; - if (standard) filter.standard = standard; - if (chapterName) filter.chapterName = chapterName; + if (subjectName) { + filter.subjectName = subjectName; + } - const topics = await Topic.find(filter); + if (standard) { + filter.standard = standard; + } - if (topics.length === 0) { - return res.status(404).json({ success: false, message: 'No topics found' }); + if (chapterName) { + // Split chapterName by commas to handle multiple values, trimming spaces + const chaptersArray = chapterName.split(',').map(chapter => chapter.trim()); + + // Use MongoDB $in operator for filtering by multiple chapters + filter.chapterName = { $in: chaptersArray }; } + const topics = await Topic.find(filter); + + return res.status(200).json({ success: true, topics }); } catch (error) { console.error('Error in getTopic:', error); @@ -156,6 +165,7 @@ export const getTopic = async (req, res) => { } }; + export const getTopicById = async (req, res) => { try { const { id } = req.params; diff --git a/package-lock.json b/package-lock.json index 6930394..f1f3277 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "express": "^4.19.2", "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", + "lodash": "^4.17.21", "mongoose": "^8.2.3", "serverless-http": "^3.2.0" } @@ -2498,7 +2499,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", diff --git a/package.json b/package.json index af05ad6..9f58b88 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "express": "^4.19.2", "express-validator": "^7.0.1", "jsonwebtoken": "^9.0.2", + "lodash": "^4.17.21", "mongoose": "^8.2.3", "serverless-http": "^3.2.0" }, diff --git a/routes/questionRoutes.js b/routes/questionRoutes.js index faede2c..cbfb7e7 100644 --- a/routes/questionRoutes.js +++ b/routes/questionRoutes.js @@ -9,6 +9,7 @@ import { getTotalQuestions, updateOption, updateQuestionDetails, + updateQuestionTopics, // toggleOptionTag, } from "../controller/quesController.js"; import isAuthenticated from "../middlewares/auth.js"; @@ -31,5 +32,6 @@ router.get("/get/myquestion", isAuthenticated, getMyQuestions) router.get("/get/users", isAuthenticated, allUser); router.get("/get/totalquestion", isAuthenticated, getTotalQuestions); router.put('/updatequestion/:questionId', updateQuestionDetails); +router.put('/updatequestiontopic', updateQuestionTopics) export default router;