Accept messy input#
Schedule project data that strict validation rejects, such as data with duplicate ids or missing dates, and get a list of every fix the engine made.
By default a Project uses Validation.STRICT and throws InputException for the first invalid field. Validation.REPAIR coerces the data into valid input instead, then schedules the result.
Steps#
-
Create the project with repair validation:
var project ; -
Pass your data to
Project#solveas usual.Project#analyzewith non-null data repairs it the same way.Engine.schedulealways uses strict validation, so use aProjecthere. -
Read the
Note.Repairedentries inresult.notesand show them to the user, or log them. Each one has apathand amessage.
The full example below has four kinds of problems: a duplicate id, a task with no dates, a dated constraint with no date, and a task whose start, end and duration disagree. Strict validation rejects this data. Repair schedules it:
;
;
What repair changes#
Repair reports each change below as one Note.Repaired, with the field path in path:
- Duplicate ids in
tasks,links,resources,assignmentsandcalendars: the last record replaces the earlier one and takes its position. The note is attasks[i].id, whereiis the index of the duplicate in your input. - A task with no start and no end gets
startDateset toconfig.projectStart, or to the earliest date of any task ifprojectStartisn't set. - A leaf task (not a milestone) with no
durationand only one date getsduration1. - Start, end and duration that disagree: the dates win, and the duration becomes the working time between them. If the end is in non-working time, or the dates were computed on a different calendar, repair drops the duration and resolves the task from its dates. An end that falls inside a pause moves to the pause start, with a second note at
tasks[i].end_date. - A summary with only one date gets the other date computed from that date and its duration.
- A dated constraint (
SNET,SNLT,FNET,FNLT,MSO,MFO) withoutconstraintDateis cleared. - Invalid
pauseson a task: the whole list is cleared, with a note attasks[i].pauses. - An invalid assignment
offsetordurationis cleared, so the assignment books the whole task.
A task that already has two of start, end and duration is valid input. Repair doesn't touch it and doesn't report it.
Note paths use the JSON field names (start_date, constraint_type), not the Java field names.
Only the duplicate-id notes use input indexes. Every other note indexes the deduplicated list. In the example, the two build records become one, so deploy is tasks[4] in your input but tasks[3] in its note.
Repair works on the project's own copy of the data. The ProjectData you pass in isn't changed.
What repair doesn't fix#
- An entirely undated project, with no
config.projectStartand no task that has a date, still throwsInputExceptionattasks[i].start_date. The engine never falls back to today's date. - Every other invalid input, such as a cycle in the
parenthierarchy or a calendar with no working time, still throwsInputException. See Error handling. - If repair or validation fails,
solvethrows and the project keeps its previous state.
Result#
The example prints the fixes, then the scheduled dates (the date lines may come in a different order):
tasks[3].id: duplicate id replaced the earlier record, retaining its position
tasks[2].constraint_type: dated constraint without date cleared
tasks[2].start_date: missing date defaulted to project anchor
tasks[2].duration: missing duration defaulted to one
tasks[3].duration: duration recomputed from supplied start and end
design 2026-01-01 00:00..2026-01-04 00:00 3.0
build 2026-01-01 00:00..2026-01-03 00:00 2.0
review 2026-01-01 00:00..2026-01-02 00:00 1.0
deploy 2026-01-01 00:00..2026-01-04 00:00 3.0
build uses the second record's duration of 2, and deploy has a duration of 3, taken from its dates. With new Project(), the same solve call throws InputException.