add new changes

This commit is contained in:
AD2025
2025-11-20 00:39:00 +02:00
parent 37b4d565b1
commit b2c564225e
12 changed files with 2734 additions and 34 deletions

View File

@@ -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