add: ver()

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-08 11:31:40 +08:00
parent 0e4cd86119
commit bb7e8fde08
29 changed files with 2234 additions and 780 deletions
+32
View File
@@ -0,0 +1,32 @@
/***********************************************************************************************************************************************************************
* File Name: course_units.mdl.js
* Type of Program: Model (junction)
* Description: Attaches a standalone Unit to a Course. A Unit can live in many
* Courses; per-course ordering lives here (order_index), not on the
* Unit itself. Detaching removes the row — the Unit survives.
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jul. 7, 2026
***********************************************************************************************************************************************************************/
const { DataTypes } = require("sequelize");
const sequelize = require("../../config/db.config");
const CourseUnit = sequelize.define("CourseUnit", {
course_unit_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
course_id: { type: DataTypes.BIGINT, allowNull: false },
unit_id: { type: DataTypes.BIGINT, allowNull: false },
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
createdBy: { type: DataTypes.BIGINT, allowNull: true },
updatedBy: { type: DataTypes.BIGINT, allowNull: true },
}, {
tableName: "course_units",
timestamps: true,
indexes: [
{ unique: true, fields: ["course_id", "unit_id"], name: "uq_course_units_course_unit" },
{ fields: ["course_id"], name: "idx_course_units_course_id" },
{ fields: ["unit_id"], name: "idx_course_units_unit_id" },
{ fields: ["order_index"], name: "idx_course_units_order" },
],
});
module.exports = CourseUnit;
+21 -4
View File
@@ -3,6 +3,8 @@
const { Course, CourseProductCategory } = require("./courses.mdl");
const Unit = require("./units.mdl");
const Lesson = require("./lessons.mdl");
const CourseUnit = require("./course_units.mdl");
const UnitLesson = require("./unit_lessons.mdl");
const LessonPage = require("./lesson_page.mdl");
const CourseObjective = require("./course_objective.mdl");
const LessonObjective = require("./lesson_objective.mdl");
@@ -44,10 +46,27 @@ LessonReadingProgress.belongsTo(Lesson, { foreignKey: 'lesson_id', as: 'lesso
mdl_Users.hasMany(LessonReadingProgress, { foreignKey: 'user_id', as: 'lessonReadingProgress' });
Lesson.hasMany(LessonReadingProgress, { foreignKey: 'lesson_id', as: 'readingProgress' });
// ── Course ⇄ Unit / Unit ⇄ Lesson (junctions) ─────────────────────────────────
// Units and Lessons are standalone entities. Membership + ordering live on the
// course_units / unit_lessons junction rows (order_index).
Course.belongsToMany(Unit, { through: CourseUnit, foreignKey: "course_id", otherKey: "unit_id", as: "units" });
Unit.belongsToMany(Course, { through: CourseUnit, foreignKey: "unit_id", otherKey: "course_id", as: "courses" });
Unit.belongsToMany(Lesson, { through: UnitLesson, foreignKey: "unit_id", otherKey: "lesson_id", as: "lessons" });
Lesson.belongsToMany(Unit, { through: UnitLesson, foreignKey: "lesson_id", otherKey: "unit_id", as: "units" });
// Direct junction access (attach / detach / reorder / count queries)
Course.hasMany(CourseUnit, { as: "unitLinks", foreignKey: "course_id" });
CourseUnit.belongsTo(Course, { as: "course", foreignKey: "course_id" });
CourseUnit.belongsTo(Unit, { as: "unit", foreignKey: "unit_id" });
Unit.hasMany(CourseUnit, { as: "courseLinks", foreignKey: "unit_id" });
Unit.hasMany(UnitLesson, { as: "lessonLinks", foreignKey: "unit_id" });
UnitLesson.belongsTo(Unit, { as: "unit", foreignKey: "unit_id" });
UnitLesson.belongsTo(Lesson, { as: "lesson", foreignKey: "lesson_id" });
Lesson.hasMany(UnitLesson, { as: "unitLinks", foreignKey: "lesson_id" });
// ── Course ────────────────────────────────────────────────────────────────────
Course.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
Course.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
Course.hasMany(Unit, { as: "units", foreignKey: "course_id" });
Course.hasMany(CourseObjective, { as: "objectives", foreignKey: "course_id" });
Course.hasMany(CoursePrerequisite, { as: "prerequisites", foreignKey: "course_id" });
Course.hasOne(CourseAssessment, { as: "assessment", foreignKey: "course_id" });
@@ -61,14 +80,11 @@ Course.belongsToMany(mdl_Category, { through: CourseProductCategory, foreignKey:
mdl_Category.belongsToMany(Course, { through: CourseProductCategory, foreignKey: 'category_id', otherKey: 'course_id', as: 'courses' });
// ── Unit ──────────────────────────────────────────────────────────────────────
Unit.belongsTo(Course, { as: "course", foreignKey: "course_id" });
Unit.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
Unit.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
Unit.hasMany(Lesson, { as: "lessons", foreignKey: "unit_id" });
Unit.hasOne(UnitQuiz, { as: "quiz", foreignKey: "unit_id" });
// ── Lesson ────────────────────────────────────────────────────────────────────
Lesson.belongsTo(Unit, { as: "unit", foreignKey: "unit_id" });
Lesson.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
Lesson.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
Lesson.hasOne(LessonPage, { as: "page", foreignKey: "lesson_id" });
@@ -114,6 +130,7 @@ UnitQuiz.hasMany(QuizSession, { as: "sessions", foreignKey: "quiz_id" });
module.exports = {
Course, CourseProductCategory,
Unit, Lesson, LessonPage,
CourseUnit, UnitLesson,
CourseObjective, LessonObjective,
CoursePrerequisite, CourseAssessment,
UnitQuiz, QuizQuestion, QuizOption, QuizAttempt,
+7 -4
View File
@@ -27,7 +27,8 @@ const computedAttributes = [
literal: `(
SELECT CAST(COUNT(*) AS INTEGER)
FROM "units"
WHERE "units"."course_id" = "Course"."course_id"
INNER JOIN "course_units" ON "course_units"."unit_id" = "units"."unit_id"
WHERE "course_units"."course_id" = "Course"."course_id"
AND "units"."deletedAt" IS NULL
)`,
filterable: false,
@@ -38,10 +39,12 @@ const computedAttributes = [
type: "number",
order: 6,
literal: `(
SELECT CAST(COUNT(*) AS INTEGER)
SELECT CAST(COUNT(DISTINCT "lessons"."lesson_id") AS INTEGER)
FROM "lessons"
INNER JOIN "units" ON "lessons"."unit_id" = "units"."unit_id"
WHERE "units"."course_id" = "Course"."course_id"
INNER JOIN "unit_lessons" ON "unit_lessons"."lesson_id" = "lessons"."lesson_id"
INNER JOIN "units" ON "unit_lessons"."unit_id" = "units"."unit_id" AND "units"."deletedAt" IS NULL
INNER JOIN "course_units" ON "course_units"."unit_id" = "units"."unit_id"
WHERE "course_units"."course_id" = "Course"."course_id"
AND "lessons"."deletedAt" IS NULL
)`,
filterable: false,
@@ -28,13 +28,13 @@ const LessonReadingProgress = sequelize.define('LessonReadingProgress', {
},
course_id: {
type: DataTypes.BIGINT,
allowNull: false,
allowNull: true, // NULL when the lesson is read standalone (outside any course)
references: { model: 'courses', key: 'course_id' },
onDelete: 'CASCADE',
},
unit_id: {
type: DataTypes.BIGINT,
allowNull: false,
allowNull: true, // NULL when the lesson is read standalone (outside any unit)
references: { model: 'units', key: 'unit_id' },
onDelete: 'CASCADE',
},
+4 -6
View File
@@ -1,17 +1,17 @@
const { DataTypes } = require("sequelize");
const sequelize = require("../../config/db.config");
// Standalone entity — no unit_id / order_index here. A Lesson is attached to
// zero or more Units through unit_lessons, where per-unit ordering lives.
const Lesson = sequelize.define("Lesson", {
lesson_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true, hidden: true, order: 0 },
uuid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, unique: true, hidden: true, order: 0 },
unit_id: { type: DataTypes.BIGINT, allowNull: false, hidden: true, order: 0 },
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title", hidden: false, order: 1 },
description: { type: DataTypes.TEXT, allowNull: true, label: "Description", hidden: false, order: 2 },
duration_seconds: { type: DataTypes.INTEGER, allowNull: true, defaultValue: 0 }, // computed from blocks on save
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, label: "Order", hidden: false, order: 3 },
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Updated By" },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
@@ -21,10 +21,8 @@ const Lesson = sequelize.define("Lesson", {
paranoid: true,
indexes: [
{ fields: ["uuid"] },
{ fields: ["unit_id"] },
{ fields: ["order_index"] },
{ fields: ["deletedAt"] },
],
});
module.exports = Lesson;
module.exports = Lesson;
+32
View File
@@ -0,0 +1,32 @@
/***********************************************************************************************************************************************************************
* File Name: unit_lessons.mdl.js
* Type of Program: Model (junction)
* Description: Attaches a standalone Lesson to a Unit. A Lesson can live in many
* Units; per-unit ordering lives here (order_index), not on the
* Lesson itself. Detaching removes the row — the Lesson survives.
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jul. 7, 2026
***********************************************************************************************************************************************************************/
const { DataTypes } = require("sequelize");
const sequelize = require("../../config/db.config");
const UnitLesson = sequelize.define("UnitLesson", {
unit_lesson_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
unit_id: { type: DataTypes.BIGINT, allowNull: false },
lesson_id: { type: DataTypes.BIGINT, allowNull: false },
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 },
createdBy: { type: DataTypes.BIGINT, allowNull: true },
updatedBy: { type: DataTypes.BIGINT, allowNull: true },
}, {
tableName: "unit_lessons",
timestamps: true,
indexes: [
{ unique: true, fields: ["unit_id", "lesson_id"], name: "uq_unit_lessons_unit_lesson" },
{ fields: ["unit_id"], name: "idx_unit_lessons_unit_id" },
{ fields: ["lesson_id"], name: "idx_unit_lessons_lesson_id" },
{ fields: ["order_index"], name: "idx_unit_lessons_order" },
],
});
module.exports = UnitLesson;
+1 -1
View File
@@ -28,7 +28,7 @@ const UnitReadingProgress = sequelize.define('UnitReadingProgress', {
},
course_id: {
type: DataTypes.BIGINT,
allowNull: false,
allowNull: true, // NULL when the unit is read standalone (outside any course)
references: { model: 'courses', key: 'course_id' },
onDelete: 'CASCADE',
},
+4 -6
View File
@@ -1,14 +1,14 @@
const { DataTypes } = require("sequelize");
const sequelize = require("../../config/db.config");
// Standalone entity — no course_id / order_index here. A Unit is attached to
// zero or more Courses through course_units, where per-course ordering lives.
const Unit = sequelize.define("Unit", {
unit_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true, hidden: true, order: 0, filterable: false },
uuid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, unique: true, hidden: true, order: 0, filterable: false },
course_id: { type: DataTypes.BIGINT, allowNull: false, hidden: true, order: 0, filterable: false },
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title", hidden: false, order: 1, filterable: true },
description: { type: DataTypes.TEXT, allowNull: true, label: "Description", hidden: true, order: 0, filterable: false },
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, label: "Order", hidden: false, order: 2, filterable: false },
duration_seconds: { type: DataTypes.INTEGER, allowNull: true, defaultValue: 0, hidden: false, order: 3, filterable: false },
duration_seconds: { type: DataTypes.INTEGER, allowNull: true, defaultValue: 0, hidden: false, order: 2, filterable: false },
createdBy: { type: DataTypes.BIGINT, allowNull: true, filterable: true },
updatedBy: { type: DataTypes.BIGINT, allowNull: true, filterable: true },
deletedBy: { type: DataTypes.BIGINT, allowNull: true, filterable: true },
@@ -18,10 +18,8 @@ const Unit = sequelize.define("Unit", {
paranoid: true,
indexes: [
{ fields: ["uuid"] },
{ fields: ["course_id"] },
{ fields: ["order_index"] },
{ fields: ["deletedAt"] },
],
});
module.exports = Unit;
module.exports = Unit;