Last active
June 5, 2022 23:21
-
-
Save DimitryDushkin/682f39be5c32a90449888a46ec499d43 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const scheduleTasks = ( | |
inputTasks: Array<Task>, | |
today: Date = getNowDate() | |
): Array<Task> => { | |
const dayBeforeToday = subtractDays(today, 1); | |
const tasks: Array<Task> = inputTasks.map((t) => ({ ...t })); | |
const tasksById: TasksById = Object.fromEntries(tasks.map((t) => [t.id, t])); | |
const graph = makeGraphFromTasks(tasks); | |
let cyclesToFullyUpdateDates = 1; | |
// 1. Remove cyclic dependencies | |
removeCyclicDependencies(graph, tasks); | |
// 2. Initial update of all tasks start and ends days taking into account business days | |
for (const task of tasks) { | |
updateTaskDatesByStart(task, today, true); | |
} | |
// Repeat until dates remains unchanged, max graph.size times. | |
// Similar to optimization in Bellman-Ford algorithm | |
// @see https://en.wikipedia.org/wiki/Bellman–Ford_algorithm#Improvements | |
for (let i = 0; i < tasks.length; i++) { | |
let isAnyTaskTimelineChanged = false; | |
for (const [taskId] of dfs(graph)) { | |
const task = tasksById[taskId]; | |
// if blockedBy task not in initial data set | |
if (task === undefined) { | |
continue; | |
} | |
const blockedByTasks = Array.from(graph.get(task.id) ?? []) | |
.map((blockedById) => tasksById[blockedById]) | |
// do not take into account tasks not in graph | |
.filter(Boolean); | |
const blockedByEndDates = blockedByTasks.map((t) => t.end); | |
// add dayBeforeToday by default, so task without blockedBy starts on today | |
blockedByEndDates.push(dayBeforeToday); | |
// Start task on the next day after previous (blockedBy) tasks ends | |
const maxBlockedByEndDate = addDays(maxDateTime(blockedByEndDates), 1); | |
const isTaskTimelineUpdated = updateTaskDatesByStart( | |
task, | |
maxBlockedByEndDate | |
); | |
if (isTaskTimelineUpdated) { | |
isAnyTaskTimelineChanged = true; | |
} | |
} | |
if (isAnyTaskTimelineChanged === false) { | |
break; | |
} | |
cyclesToFullyUpdateDates++; | |
if (isAnyTaskTimelineChanged && i === tasks.length - 1) { | |
console.error( | |
'It\'s not enought "tasks.length" interations to fully schedule all tasks!' | |
); | |
} | |
} | |
console.debug( | |
`Cycles to fully update dates ${cyclesToFullyUpdateDates}/${tasks.length}` | |
); | |
// for better representation | |
return toposort(graph, tasks); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment