Skip to content

Instantly share code, notes, and snippets.

@DimitryDushkin
Last active June 5, 2022 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DimitryDushkin/682f39be5c32a90449888a46ec499d43 to your computer and use it in GitHub Desktop.
Save DimitryDushkin/682f39be5c32a90449888a46ec499d43 to your computer and use it in GitHub Desktop.
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