mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
64 lines
2.5 KiB
JavaScript
64 lines
2.5 KiB
JavaScript
"use strict";
|
|
|
|
/***********************************************************************************************************************************************************************
|
|
* File Name: taskPrerequisites.util.js
|
|
* Type of Program: Utility
|
|
* Description: Cycle detection for the explicit Task ↔ Task prerequisite graph
|
|
* (task_prerequisites junction table). A task's prerequisite set is
|
|
* only meaningful if the graph stays a DAG — this checks whether
|
|
* persisting a proposed edge set would introduce a cycle.
|
|
*
|
|
* Author: Kenneth Obsequio (@lash0000)
|
|
* Date Created: Jul. 17, 2026
|
|
***********************************************************************************************************************************************************************/
|
|
|
|
const { TaskPrerequisite } = require("../../models/task/task.mdl");
|
|
|
|
/**
|
|
* Would setting `taskId`'s prerequisites to `newPrereqIds` introduce a cycle?
|
|
*
|
|
* Loads every existing task_prerequisites edge for the task list, overlays the
|
|
* proposed edges for `taskId` (replacing whatever it currently points to), then
|
|
* DFS from `taskId` looking for a path back to itself.
|
|
*/
|
|
async function wouldCreateCycle(taskId, newPrereqIds, taskListId, transaction) {
|
|
const { Task } = require("../../models/task/task.mdl");
|
|
|
|
const siblingTasks = await Task.findAll({
|
|
where: { task_list_id: taskListId },
|
|
attributes: ["task_id"],
|
|
transaction,
|
|
});
|
|
const siblingIds = siblingTasks.map((t) => t.task_id);
|
|
|
|
const existingEdges = await TaskPrerequisite.findAll({
|
|
where: { task_id: siblingIds },
|
|
attributes: ["task_id", "prerequisite_task_id"],
|
|
transaction,
|
|
});
|
|
|
|
// adjacency: task_id -> Set(prerequisite_task_id) ("depends on")
|
|
const adjacency = new Map();
|
|
for (const { task_id, prerequisite_task_id } of existingEdges) {
|
|
if (task_id === taskId) continue; // overlay taskId's edges with the proposed set below
|
|
if (!adjacency.has(task_id)) adjacency.set(task_id, new Set());
|
|
adjacency.get(task_id).add(prerequisite_task_id);
|
|
}
|
|
adjacency.set(taskId, new Set(newPrereqIds));
|
|
|
|
// DFS from taskId looking for a path back to taskId
|
|
const visited = new Set();
|
|
const stack = [...adjacency.get(taskId)];
|
|
while (stack.length) {
|
|
const current = stack.pop();
|
|
if (current === taskId) return true;
|
|
if (visited.has(current)) continue;
|
|
visited.add(current);
|
|
for (const next of adjacency.get(current) ?? []) stack.push(next);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
module.exports = { wouldCreateCycle };
|