Project#roster#

Reads the accepted state of a Project without skills: scheduled leaf tasks, resources and active assignments, in model order. Source data for a custom Slot list for suggestAssignments. Needs a solved project with no unsolved edits.

Signature#

public Roster roster()

Synchronous and CPU-only. Doesn't change the project. Not synchronized: serialize calls on one instance.

Returns#

A new Roster.

Errors#

All exceptions are unchecked.

Error When
ProjectException with code == ProjectError.NO_BASELINE The project is unsolved or has unsolved edits, for example after apply

Example#

Build one slot per scheduled task that has no assignment yet, open to every resource.

import java.util.List;
import java.util.stream.Collectors;
import org.dhtmlx.plen.*;

public class RosterExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 8, 10, 8, 0);
    ProjectData data = new ProjectData()
        .withTasks(List.of(
            new TaskData(Id.str("a")).withStartDate(monday).withDuration(2.0),
            new TaskData(Id.str("b")).withStartDate(monday).withDuration(2.0)))
        .withResources(List.of(
            new ResourceData().withId(Id.str("dev1")),
            new ResourceData().withId(Id.str("dev2"))))
        .withAssignments(List.of(
            new AssignmentData().withId(Id.str("x")).withTask(Id.str("a")).withResource(Id.str("dev1")).withValue(1.0)));

    Project project = new Project();
    project.solve(data, new SolveOptions());

    Roster roster = project.roster();
    List<Slot> slots = roster.tasks.stream()
        .filter(task -> roster.assignments.stream().noneMatch(a -> a.task.equals(task)))
        .map(task -> new Slot(Id.str("slot-" + task), task, roster.resources))
        .collect(Collectors.toList());

    SuggestResult result = project.suggestAssignments(slots, new SuggestOptions());
    System.out.println(slots.size() + " slot(s), " + result.steps.size() + " step(s)");
    // 1 slot(s), 1 step(s)
  }
}