ApplyReport#

The outcome of one Project.Apply call. It lists which operations of the SchedulingChanges were applied and which were rejected, the records the edit removed, generated or dropped as a side effect, and the project's revision afterwards.

Apply returns a new instance on every call.

Declaration#

public sealed class ApplyReport : IDeepCloneable<ApplyReport>, IEquatable<ApplyReport>
{
    public List<AppliedOperation> Applied { get; set; } = [];
    public List<RejectedOperation> Rejected { get; set; } = [];
    public ChangeEffects Effects { get; set; } = new();
    public bool NeedsResync { get; set; }
    public ulong Revision { get; set; }

    public ApplyReport DeepClone();
    public bool Equals(ApplyReport? other);
    public override string ToString();
}

Properties#

Property Type Description
Applied List<AppliedOperation> Operations that were applied, in execution order.
Rejected List<RejectedOperation> Operations that were rejected, in execution order. Empty when everything was applied.
Effects ChangeEffects Records removed, generated or dropped by the applied operations.
NeedsResync bool true when Rejected isn't empty.
Revision ulong The project's revision counter after this call.

Applied#

public List<AppliedOperation> Applied { get; set; } = [];

One entry per applied operation, in the order the operations ran. Each entry's Path names the operation. For the execution order, see Operation order.

Rejected#

public List<RejectedOperation> Rejected { get; set; } = [];

One entry per rejected operation, with its path and the reason. A rejected operation leaves no partial edit; operations after it still run.

Effects#

public ChangeEffects Effects { get; set; } = new();

Side effects of the applied operations only. A rejected operation contributes nothing here.

NeedsResync#

public bool NeedsResync { get; set; }

true exactly when at least one operation was rejected. From then on, Apply, Solve(null, …) and Level throw a ProjectException with Code == ProjectError.NeedsResync until a full Solve(data, …). See Recover from rejected changes.

Revision#

public ulong Revision { get; set; }

The project's revision counter after the apply. Every Apply raises it, so a higher value means a later state of the same project. A Clone() starts from the source's counter.

Methods#

DeepClone#

public ApplyReport DeepClone()

Returns a copy that shares no list or nested record with this instance.

Equals#

public bool Equals(ApplyReport? other)

Value equality: the three lists element by element, Effects by value, NeedsResync and Revision by ==.

ToString#

public override string ToString()

Returns a debug dump of all five properties.

AppliedOperation#

One applied operation, identified by its position in the change set.

public sealed class AppliedOperation : IDeepCloneable<AppliedOperation>, IEquatable<AppliedOperation>
{
    public required string Path { get; set; }

    public AppliedOperation DeepClone();
    public bool Equals(AppliedOperation? other);
    public override string ToString();
}
Property Type Description
Path string The operation's bucket, list and index in the change set, in the form {bucket}.{list}[{index}]: tasks.update[0], links.add[2], assignments.remove[1]. Buckets: tasks, links, resources, assignments; lists: add, update, remove.

ChangeEffects#

Records an apply removed, generated or dropped beyond the ones the change set names directly. Use it to keep a client-side copy of the project in sync.

public sealed class ChangeEffects : IDeepCloneable<ChangeEffects>, IEquatable<ChangeEffects>
{
    public List<Id> RemovedTasks { get; set; } = [];
    public List<Id> RemovedLinks { get; set; } = [];
    public List<Id> GeneratedLinks { get; set; } = [];
    public List<Id> DroppedLinks { get; set; } = [];
    public List<Id> RemovedAssignments { get; set; } = [];
    public List<Id> GeneratedAssignments { get; set; } = [];
    public List<Id> DroppedAssignments { get; set; } = [];

    public ChangeEffects DeepClone();
    public bool Equals(ChangeEffects? other);
    public override string ToString();
}

Every list holds Id values and is empty when nothing of that kind happened.

Property Type Description
RemovedTasks List<Id> Tasks deleted by tasks.remove, including every descendant of a removed task.
RemovedLinks List<Id> Links deleted by links.remove, plus links whose source or target was a removed task.
GeneratedLinks List<Id> Ids created for links.add entries without an Id, in the form $generated_link_{n}.
DroppedLinks List<Id> Links that stay in the data but were taken out of scheduling by this apply, for example because an endpoint became unscheduled. See Links that get dropped.
RemovedAssignments List<Id> Assignments deleted by assignments.remove, plus assignments of removed tasks and removed resources. Cascaded assignments without an id aren't listed.
GeneratedAssignments List<Id> Ids created for assignments.add entries without an Id, in the form $generated_assignment_{n}.
DroppedAssignments List<Id> Assignments that stay in the data but were taken out of scheduling by this apply because their task became unscheduled. They return when the task is scheduled again.

DeepClone() copies every list. Equals compares the seven lists element by element.

Example#

Remove a task that has a link and an assignment, and add a link without an id.

using Plen;

var monday = Moment.FromYmdHm(2026, 8, 10, 8, 0) ?? throw new InvalidOperationException("invalid date");

var data = new ProjectData
{
    Tasks =
    [
        new TaskData("a") { StartDate = monday, Duration = 1.0 },
        new TaskData("b") { StartDate = monday, Duration = 2.0 },
        new TaskData("c") { StartDate = monday, Duration = 1.0 },
    ],
    Links = [new LinkData { Id = "ab", Source = "a", Target = "b" }],
    Resources = [new ResourceData { Id = "dev" }],
    Assignments = [new AssignmentData { Id = "as1", Task = "b", Resource = "dev", Value = 1.0 }],
};

var project = new Project();
project.Solve(data, new SolveOptions());

var changes = new SchedulingChanges();
changes.Tasks.Remove.Add("b");
changes.Links.Add.Add(new LinkAdd { Source = "a", Target = "c" });

var report = project.Apply(changes);

foreach (var op in report.Applied)
    Console.WriteLine($"applied {op.Path}");          // tasks.remove[0], links.add[0]

var effects = report.Effects;
Console.WriteLine(string.Join(", ", effects.RemovedTasks));         // b
Console.WriteLine(string.Join(", ", effects.RemovedLinks));         // ab
Console.WriteLine(string.Join(", ", effects.RemovedAssignments));   // as1
Console.WriteLine(string.Join(", ", effects.GeneratedLinks));       // $generated_link_1