Import from dhtmlxGantt#
Load a dhtmlxGantt project into Planning Engine and get the same task dates the gantt auto-scheduler would give you.
Planning Engine's scheduling step is a port of the dhtmlxGantt v2 auto-scheduling engine, and its input uses gantt's field names (start_date, duration, parent, constraint_type, auto_scheduling). Most of the import copies fields one to one. The steps below cover the places where a straight copy gives different dates or fails validation.
Prerequisites#
- Your gantt tasks, links, resources and assignments are parsed into your own Rust records.
plen-corehas no JSON input. For typed JSON input, use theplen-wirecrate or theplenCLI. - You know each task's effective calendar, as gantt resolves it.
- The example parses dates with
chrono(cargo add chrono).
Steps#
-
Set the scheduling config to gantt's model. Use
CalendarMode::Taskfor task calendars only, orCalendarMode::TaskInheritedif gantt hasinherit_calendar: true. Copy gantt'swork_timeintouse_calendarsexplicitly. If you leaveuse_calendarsout, Planning Engine schedules on calendars:use ; let gantt_work_time = true; // gantt `config.work_time` let config = SchedulingConfig ;Set
root_idonly when gantt's virtual root isn't0. The other auto-scheduling options (apply_constraints,gap_behavior,move_projects,schedule_from_end,project_start,use_progress) keep their gantt names. -
Add one
CalendarDataper calendar your tasks use, and set each task'scalendar_idto its effective calendar. gantt picks that calendar in its adapter: the task's own, else its resource's, else a calendar merged from all its resources, else the parent's. Send the result, not the rules. A merged resource calendar goes in once, under its synthetic id. Don't repeat gantt's precedence or merging in your import code. -
Map each task to a
TaskData:- gantt
type: "project"becomesTaskType::Summary. A summary takes bothstart_dateandend_date, or neither. Drop itsduration, or strict validation rejects the task. - A
parentequal toroot_idmeans "no parent". You can copy it as is. - A leaf task needs two of
start_date,end_dateandduration. If you send all three, they must agree. - If your gantt event handlers cancel auto-scheduling for a task, set
auto_scheduling: falseon it. The planner never moves it.
- gantt
-
Map gantt link
typevalues"0","1","2"and"3"toLinkType::Fs,Ss,FfandSf. Copylagand leavelag_unitasNone, because gantt measures lag inconfig.duration_unit. -
Convert each assignment to a slice of its task's work. Don't send gantt's
mode.defaultandfixedDuration:delaybecomesoffsetanddurationstaysduration. Leave bothNoneto book the whole task.fixedDates: setoffsetanddurationto the worked time its dates cover, measured from the task start.
A slice ends at the task end. gantt's
fixedDurationcan run past it. -
Solve the data with
Project::solveand write the changed fields fromresult.updatesback into gantt. The updates include summaries, which the gantt JS adapter leaves to gantt core. Apply those too, or your summary spans go stale.
The full example below imports a summary with two tasks, a milestone, two links, a part-time calendar and one default-mode assignment:
use NaiveDateTime;
use ;
// Records as your server holds them after parsing gantt's JSON.
Keep gantt's triggers on your side#
Planning Engine has no UI triggers and no events. Your code decides when to schedule.
- Call
applyand thensolve(None, ...)wherever gantt would run auto-scheduling: after the user adds a link, drags a task, or saves the lightbox. See Data Updates. gantt.autoSchedule(id, inclusive)isSolveOptions::subtree. See Reschedule a branch.- Change sets can't add or edit calendars. If an assignment change gives a task a new merged calendar, send the whole project again with
solve(Some(data), ...). - gantt core rewrites descendant dates when inherited calendars or effort change. Forward those date and duration edits as ordinary
TaskUpdatechanges. Planning Engine doesn't recreate gantt's effort rules.
Differences to expect#
- Strict validation rejects input that gantt coerces: duplicate ids, a dated constraint with no
constraint_date, a leaf task with fewer than two of start, end and duration, a dateless summary with no dated children. To get gantt's coercions with a report of each fix, see Accept messy input. week,monthandyearduration units and calendars withcustomWeeksare rejected. A gantt merged calendar can containcustomWeeks, so such projects can't be imported yet.- Dates are naive local date-times with no timezone and no DST. gantt's JS engine uses the host time zone, so its arithmetic follows DST. Convert dates to naive local time before you build the data.
result.noteslists the links, tasks and assignments the engine dropped. gantt drops them silently.- Three cases give different dates on purpose. With
project_constraint: true, children inherit the parent's constraint. gantt's engine ignores it. Withuse_progress, a completed child of a moving summary stays in place. gantt drags it along. Withdescendant_links: truein block mode, gantt fails with a cycle error and Planning Engine schedules the project. See Links, constraints and summaries. - Summary-to-summary links in block mode, combined with leaf links between the same summaries, can give different dates. On very large generated graphs the call can panic. See Error handling.
Result#
The example prints the changed fields:
3 start_date = 2026-03-05 00:00
3 end_date = 2026-03-12 00:00
1 end_date = 2026-03-12 00:00
4 start_date = 2026-03-12 00:00
4 end_date = 2026-03-12 00:00
Task 3 starts when task 2 finishes and works Monday to Thursday only, so it ends a week later. The summary 1 grows to cover it. Task 2 didn't move, so it isn't listed. To get every date on the first load, see Load all task dates.