add new changes
This commit is contained in:
@@ -1,6 +1,209 @@
|
||||
const { Question, Category, sequelize } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* Get single question by ID (Admin only)
|
||||
* GET /api/admin/questions/:id
|
||||
* Returns complete question data including correctAnswer
|
||||
*/
|
||||
exports.getQuestionByIdAdmin = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Validate UUID format
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (!uuidRegex.test(id)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid question ID format'
|
||||
});
|
||||
}
|
||||
|
||||
// Query question with category info (including inactive questions for admin)
|
||||
const question = await Question.findOne({
|
||||
where: { id },
|
||||
attributes: [
|
||||
'id',
|
||||
'questionText',
|
||||
'questionType',
|
||||
'options',
|
||||
'correctAnswer',
|
||||
'difficulty',
|
||||
'points',
|
||||
'timesAttempted',
|
||||
'timesCorrect',
|
||||
'explanation',
|
||||
'tags',
|
||||
'keywords',
|
||||
'isActive',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
include: [
|
||||
{
|
||||
model: Category,
|
||||
as: 'category',
|
||||
attributes: ['id', 'name', 'slug', 'icon', 'color', 'guestAccessible', 'isActive']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Check if question exists
|
||||
if (!question) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Question not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Convert to JSON and add calculated fields
|
||||
const questionData = question.toJSON();
|
||||
|
||||
// Calculate accuracy
|
||||
questionData.accuracy = question.timesAttempted > 0
|
||||
? Math.round((question.timesCorrect / question.timesAttempted) * 100)
|
||||
: 0;
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: questionData,
|
||||
message: 'Question retrieved successfully'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in getQuestionByIdAdmin:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'An error occurred while retrieving the question',
|
||||
error: process.env.NODE_ENV === 'development' ? error.message : undefined
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all questions with pagination, filtering and search (Admin only)
|
||||
* GET /api/admin/questions?page=1&limit=10&search=javascript&category=uuid&difficulty=easy&sortBy=createdAt&order=DESC
|
||||
*/
|
||||
exports.getAllQuestions = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
search = '',
|
||||
category = '',
|
||||
difficulty = '',
|
||||
sortBy = 'createdAt',
|
||||
order = 'DESC'
|
||||
} = req.query;
|
||||
|
||||
// Validate and parse pagination
|
||||
const pageNumber = Math.max(parseInt(page, 10) || 1, 1);
|
||||
const pageSize = Math.min(Math.max(parseInt(limit, 10) || 10, 1), 100);
|
||||
const offset = (pageNumber - 1) * pageSize;
|
||||
|
||||
// Build where conditions for questions
|
||||
const whereConditions = {};
|
||||
|
||||
// Search filter (question text or explanation)
|
||||
if (search && search.trim().length > 0) {
|
||||
whereConditions[Op.or] = [
|
||||
{ questionText: { [Op.like]: `%${search.trim()}%` } },
|
||||
{ explanation: { [Op.like]: `%${search.trim()}%` } },
|
||||
{ tags: { [Op.like]: `%${search.trim()}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// Difficulty filter
|
||||
if (difficulty && ['easy', 'medium', 'hard'].includes(difficulty.toLowerCase())) {
|
||||
whereConditions.difficulty = difficulty.toLowerCase();
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (category) {
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (uuidRegex.test(category)) {
|
||||
whereConditions.categoryId = category;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate sort field
|
||||
const validSortFields = ['createdAt', 'updatedAt', 'questionText', 'difficulty', 'points', 'timesAttempted'];
|
||||
const sortField = validSortFields.includes(sortBy) ? sortBy : 'createdAt';
|
||||
const sortOrder = order.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
|
||||
|
||||
// Query questions with pagination
|
||||
const { count, rows: questions } = await Question.findAndCountAll({
|
||||
where: whereConditions,
|
||||
attributes: [
|
||||
'id',
|
||||
'questionText',
|
||||
'questionType',
|
||||
'options',
|
||||
'difficulty',
|
||||
'points',
|
||||
'explanation',
|
||||
'tags',
|
||||
'keywords',
|
||||
'timesAttempted',
|
||||
'timesCorrect',
|
||||
'isActive',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
include: [
|
||||
{
|
||||
model: Category,
|
||||
as: 'category',
|
||||
attributes: ['id', 'name', 'slug', 'icon', 'color', 'guestAccessible']
|
||||
}
|
||||
],
|
||||
order: [[sortField, sortOrder]],
|
||||
limit: pageSize,
|
||||
offset: offset
|
||||
});
|
||||
|
||||
// Calculate accuracy for each question
|
||||
const questionsWithAccuracy = questions.map(question => {
|
||||
const questionData = question.toJSON();
|
||||
questionData.accuracy = question.timesAttempted > 0
|
||||
? Math.round((question.timesCorrect / question.timesAttempted) * 100)
|
||||
: 0;
|
||||
|
||||
// Keep correctAnswer for admin
|
||||
return questionData;
|
||||
});
|
||||
|
||||
// Calculate pagination metadata
|
||||
const totalPages = Math.ceil(count / pageSize);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
count: questionsWithAccuracy.length,
|
||||
total: count,
|
||||
page: pageNumber,
|
||||
totalPages,
|
||||
limit: pageSize,
|
||||
filters: {
|
||||
search: search || null,
|
||||
category: category || null,
|
||||
difficulty: difficulty || null,
|
||||
sortBy: sortField,
|
||||
order: sortOrder
|
||||
},
|
||||
data: questionsWithAccuracy,
|
||||
message: `Retrieved ${questionsWithAccuracy.length} of ${count} questions`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in getAllQuestions:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'An error occurred while retrieving questions',
|
||||
error: process.env.NODE_ENV === 'development' ? error.message : undefined
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get questions by category with filtering and pagination
|
||||
* GET /api/questions/category/:categoryId?difficulty=easy&limit=10&random=true
|
||||
|
||||
Reference in New Issue
Block a user