Choreo.Planner.Analysis (Choreo v0.9.0)

Copy Markdown View Source

Graph analysis algorithms for Choreo.Planner projects.

Provides project-management-specific insights:

Summary

Functions

Returns tasks that have unresolved dependencies or blockers.

Returns tasks sorted by how many other tasks depend on them (directly or transitively).

Finds the critical path — the longest chain of dependent work by estimate.

Returns tasks with no :contains relationship (not in any milestone).

Returns tasks with status :backlog or :todo whose dependencies are all :done.

Validates structural integrity.

Converts validation 3-tuples to standard 2-tuples with human-readable messages.

Functions

blocked(planner)

@spec blocked(Choreo.Planner.t()) :: [{Yog.node_id(), map()}]

Returns tasks that have unresolved dependencies or blockers.

Done and cancelled tasks are excluded.

Examples

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:a, status: :backlog)
...> |> Choreo.Planner.add_task(:b, status: :backlog)
...> |> Choreo.Planner.depends_on(:a, :b)
iex> [{id, _}] = Choreo.Planner.Analysis.blocked(project)
iex> id
:a

bottlenecks(planner)

@spec bottlenecks(Choreo.Planner.t()) :: [{Yog.node_id(), non_neg_integer()}]

Returns tasks sorted by how many other tasks depend on them (directly or transitively).

High count = task blocks a lot of downstream work (bottleneck).

Examples

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:core)
...> |> Choreo.Planner.add_task(:a)
...> |> Choreo.Planner.add_task(:b)
...> |> Choreo.Planner.depends_on(:a, :core)
...> |> Choreo.Planner.depends_on(:b, :core)
iex> [{top_id, top_count} | _rest] = Choreo.Planner.Analysis.bottlenecks(project)
iex> top_id
:core
iex> top_count
2

critical_path(planner, opts \\ [])

@spec critical_path(
  Choreo.Planner.t(),
  keyword()
) :: {:ok, [Yog.node_id()], keyword()} | :error

Finds the critical path — the longest chain of dependent work by estimate.

If a milestone is given, only tasks under that milestone are considered.

Returns {:ok, path, total_estimate} or :error if the dependency graph contains a cycle.

Examples

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:a, estimate_hours: 2)
...> |> Choreo.Planner.add_task(:b, estimate_hours: 3)
...> |> Choreo.Planner.depends_on(:b, :a)
iex> {:ok, [:a, :b], total_estimate: 5} = Choreo.Planner.Analysis.critical_path(project)

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:a)
...> |> Choreo.Planner.add_task(:b)
...> |> Choreo.Planner.add_task(:c)
...> |> Choreo.Planner.depends_on(:b, :a)
...> |> Choreo.Planner.depends_on(:a, :c)
...> |> Choreo.Planner.depends_on(:c, :b)
iex> Choreo.Planner.Analysis.critical_path(project)
:error

orphans(planner)

@spec orphans(Choreo.Planner.t()) :: [{Yog.node_id(), map()}]

Returns tasks with no :contains relationship (not in any milestone).

Examples

iex> project = Choreo.Planner.new() |> Choreo.Planner.add_task(:t1)
iex> [{id, _}] = Choreo.Planner.Analysis.orphans(project)
iex> id
:t1

ready(planner)

@spec ready(Choreo.Planner.t()) :: [{Yog.node_id(), map()}]

Returns tasks with status :backlog or :todo whose dependencies are all :done.

Examples

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:t1, status: :todo)
iex> [{id, _}] = Choreo.Planner.Analysis.ready(project)
iex> id
:t1

validate(planner)

@spec validate(Choreo.Planner.t()) :: list()

Validates structural integrity.

Returns a list of issues like {:error, :cycle_detected, nodes} or {:warning, :unassigned_in_progress, task_id}.

Examples

iex> project = Choreo.Planner.new() |> Choreo.Planner.add_task(:a, status: :in_progress)
iex> Choreo.Planner.Analysis.validate(project)
[{:warning, :unassigned_in_progress, :a}, {:warning, :orphan_task, :a}]

iex> project = Choreo.Planner.new()
...> |> Choreo.Planner.add_task(:a)
...> |> Choreo.Planner.add_task(:b)
...> |> Choreo.Planner.add_task(:c)
...> |> Choreo.Planner.depends_on(:b, :a)
...> |> Choreo.Planner.depends_on(:a, :c)
...> |> Choreo.Planner.depends_on(:c, :b)
iex> [{:error, :cycle_detected, nodes} | _] = Choreo.Planner.Analysis.validate(project)
iex> is_list(nodes)
true
iex> :a in nodes
true

validate_messages(planner)

@spec validate_messages(Choreo.Planner.t()) :: [{:error | :warning, String.t()}]

Converts validation 3-tuples to standard 2-tuples with human-readable messages.