# `GanttEx.Scheduler`
[🔗](https://github.com/rocket4ce/gantt_ex/blob/v0.1.0/lib/gantt_ex/scheduler.ex#L1)

Auto-scheduling: moves tasks so that dependencies and constraints hold.

Tasks are processed in dependency order (see `GanttEx.Dependency`). Each
task starts at the earliest date its predecessors allow, adjusted by its
constraint:

| Constraint | Meaning                          | Effect                                      |
| ---------- | -------------------------------- | ------------------------------------------- |
| `:asap`    | as soon as possible (default)    | dependencies only                           |
| `:alap`    | as late as possible              | as late as successors and the project allow |
| `:snet`    | start no earlier than the date   | lower bound                                 |
| `:fnet`    | finish no earlier than the date  | lower bound                                 |
| `:snlt`    | start no later than the date     | upper bound (wins over dependencies)        |
| `:fnlt`    | finish no later than the date    | upper bound (wins over dependencies)        |
| `:mso`     | must start on the date           | fixed start                                 |
| `:mfo`     | must finish on the date          | fixed end                                   |

By default only tasks that violate a dependency are moved (later). With
`strict: true` every constrained task moves to its earliest possible date,
so tasks also move earlier when their predecessors do.

    iex> store = GanttEx.Store.new!(
    ...>   [
    ...>     %{id: 1, text: "Design", start_date: ~D[2025-01-06], duration: 3},
    ...>     %{id: 2, text: "Build", start_date: ~D[2025-01-07], duration: 2},
    ...>     %{id: 3, text: "Launch", start_date: ~D[2025-01-08], type: :milestone}
    ...>   ],
    ...>   [%{id: 1, source: 1, target: 2}, %{id: 2, source: 2, target: 3}]
    ...> )
    iex> {:ok, store, changes} = GanttEx.Scheduler.schedule(store)
    iex> Enum.map(changes, fn {type, task, _} -> {type, task.id} end)
    [task_updated: 2, task_updated: 3]
    iex> {GanttEx.Store.get_task(store, 2).start_date, GanttEx.Store.get_task(store, 3).start_date}
    {~N[2025-01-09 00:00:00], ~N[2025-01-11 00:00:00]}

# `compact`

```elixir
@spec compact([GanttEx.Store.change()]) :: [GanttEx.Store.change()]
```

Merges repeated `:task_updated` changes of the same task (a project can be
recomputed several times while its children move), keeping the first
position and the last value.

    iex> a = GanttEx.Task.new!(id: 1, text: "A")
    iex> b = %{a | text: "B"}
    iex> GanttEx.Scheduler.compact([{:task_updated, a, %{changes: %{start_date: 1}}}, {:task_updated, b, %{changes: %{end_date: 2}}}])
    [{:task_updated, b, %{changes: %{start_date: 1, end_date: 2}}}]

# `schedule`

```elixir
@spec schedule(GanttEx.Store.t(), keyword()) ::
  {:ok, GanttEx.Store.t(), [GanttEx.Store.change()]}
```

Schedules every task of the store.

## Options

  * `:strict` - move tasks to their earliest possible date, even earlier.
    Defaults to `false`.

Returns `{:ok, store, changes}` like the mutations of `GanttEx.Store`.

# `target_start`

```elixir
@spec target_start(GanttEx.Store.t(), GanttEx.Task.t(), boolean()) ::
  NaiveDateTime.t() | nil
```

Returns the start date `task` should have, or `nil` to keep it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
