Files
tasks-backend/migrations/20251110190953-create-quiz-sessions.js
2025-12-26 23:56:32 +02:00

204 lines
6.0 KiB
JavaScript

'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('quiz_sessions', {
id: {
type: Sequelize.CHAR(36),
primaryKey: true,
allowNull: false,
comment: 'UUID primary key'
},
user_id: {
type: Sequelize.CHAR(36),
allowNull: true,
references: {
model: 'users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: 'Foreign key to users table (null for guest quizzes)'
},
guest_session_id: {
type: Sequelize.CHAR(36),
allowNull: true,
references: {
model: 'guest_sessions',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
comment: 'Foreign key to guest_sessions table (null for user quizzes)'
},
category_id: {
type: Sequelize.CHAR(36),
allowNull: false,
references: {
model: 'categories',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'RESTRICT',
comment: 'Foreign key to categories table'
},
quiz_type: {
type: Sequelize.ENUM('practice', 'timed', 'exam'),
allowNull: false,
defaultValue: 'practice',
comment: 'Type of quiz: practice (untimed), timed, or exam mode'
},
difficulty: {
type: Sequelize.ENUM('easy', 'medium', 'hard', 'mixed'),
allowNull: false,
defaultValue: 'mixed',
comment: 'Difficulty level of questions in the quiz'
},
total_questions: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 10,
comment: 'Total number of questions in this quiz session'
},
questions_answered: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'Number of questions answered so far'
},
correct_answers: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'Number of correct answers'
},
score: {
type: Sequelize.DECIMAL(5, 2),
allowNull: false,
defaultValue: 0.00,
comment: 'Quiz score as percentage (0-100)'
},
total_points: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'Total points earned in this quiz'
},
max_points: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'Maximum possible points for this quiz'
},
time_limit: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: true,
comment: 'Time limit in seconds (null for untimed practice)'
},
time_spent: {
type: Sequelize.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
comment: 'Total time spent in seconds'
},
started_at: {
type: Sequelize.DATE,
allowNull: true,
comment: 'When the quiz was started'
},
completed_at: {
type: Sequelize.DATE,
allowNull: true,
comment: 'When the quiz was completed'
},
status: {
type: Sequelize.ENUM('not_started', 'in_progress', 'completed', 'abandoned', 'timed_out'),
allowNull: false,
defaultValue: 'not_started',
comment: 'Current status of the quiz session'
},
is_passed: {
type: Sequelize.BOOLEAN,
allowNull: true,
comment: 'Whether the quiz was passed (null if not completed)'
},
pass_percentage: {
type: Sequelize.DECIMAL(5, 2),
allowNull: false,
defaultValue: 70.00,
comment: 'Required percentage to pass (default 70%)'
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: 'Record creation timestamp'
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'),
comment: 'Record last update timestamp'
}
}, {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
comment: 'Tracks individual quiz sessions for users and guests'
});
// Add indexes for better query performance
await queryInterface.addIndex('quiz_sessions', ['user_id'], {
name: 'idx_quiz_sessions_user_id'
});
await queryInterface.addIndex('quiz_sessions', ['guest_session_id'], {
name: 'idx_quiz_sessions_guest_session_id'
});
await queryInterface.addIndex('quiz_sessions', ['category_id'], {
name: 'idx_quiz_sessions_category_id'
});
await queryInterface.addIndex('quiz_sessions', ['status'], {
name: 'idx_quiz_sessions_status'
});
await queryInterface.addIndex('quiz_sessions', ['quiz_type'], {
name: 'idx_quiz_sessions_quiz_type'
});
await queryInterface.addIndex('quiz_sessions', ['started_at'], {
name: 'idx_quiz_sessions_started_at'
});
await queryInterface.addIndex('quiz_sessions', ['completed_at'], {
name: 'idx_quiz_sessions_completed_at'
});
await queryInterface.addIndex('quiz_sessions', ['created_at'], {
name: 'idx_quiz_sessions_created_at'
});
await queryInterface.addIndex('quiz_sessions', ['is_passed'], {
name: 'idx_quiz_sessions_is_passed'
});
// Composite index for common queries
await queryInterface.addIndex('quiz_sessions', ['user_id', 'status'], {
name: 'idx_quiz_sessions_user_status'
});
await queryInterface.addIndex('quiz_sessions', ['guest_session_id', 'status'], {
name: 'idx_quiz_sessions_guest_status'
});
console.log('✅ Quiz sessions table created successfully with 21 fields and 11 indexes');
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('quiz_sessions');
console.log('✅ Quiz sessions table dropped');
}
};