Our state of the art Gantt chart


Post by Jerther »

Hi!

First and foremost, thanks a lot for the help you've given me so far. Our project is looking pretty nice with Bryntum Gantt and the integration is smooth 8-) . I'm on the last stretch of dev and this might be the most critial part of it. We used to rely on a backend scheduling engine to get when a task should start and end based on resources and calendars and I figured I could give your engine a go. So I followed and understood the guide in the documentation which is a fair introduction, and then I went ahead and integrated it in our project, inspired by the data that drives the demos.

Our use case is fairly advanced but in simple terms, pretty much all tasks we have are effort driven and for any task we have, we may have multiple assignments for multiple resources on multiple schedules, and also every resource has its own leaves. For example, for a single task we might have those assignments:
  • Employee A, 100% assigned, works on day schedule, has two leaves in the upcoming week
  • Employee B, 75% assigned, works on night schedule, has no leaves planned
  • Employee C, 100% assigned, works on night schedule, has one leave in two days
  • Employee D, 100% assigned, works on weekend schedule, has no leaves.
Now the calendar store structure allows this pretty easily. I figured I could have one main calendar per schedule (day, night and weekend) and then have children calendars with leaves for each employee.

But that does not quite work. Or I missed something? Apparently I must set a calendar on a task for the scheduling to work, right? The task begins depending on what calendar I choose and in the example above, there is no single good choice. I expect the task to start as early as possible (from the constraint) according to all the resources availabilities, and I expect its duration to take the employees leaves into account.

Here's a sample of test data. Notice the calendar structure: One main calendar per shift where intervals are working times (isWorking: true) and also global leaves (isWorking: false). The main calendars then have one child per resource where the intervals are all leaves (isWorking: false) (sick leaves for example)
{
	"success": true,
	"type": "load",
	"project": {
		"name": "Year 20XX",
		"startDate": "2020-04-01 00:00:00"
	},
	"tasks": {
		"rows": [{
			"id": 77,
			"name": "Eat all the potatoes!",
			"startDate": "2020-04-02 00:00:00",
			"endDate": "2020-04-03 12:00:00",
			"duration": 1.333333333,
			"constraintDate": "2020-04-02 00:00:00",
			"manuallyScheduled": false,
			"rollup": false,
			"initiallyExpanded": true,
			"effortDriven": true,
			"constraintType": "startnoearlierthan",
			"effortUnit": "hour",
			"schedulingMode": "FixedUnits",
			"expanded": true,
			"effort": 72.0,
			"percentDone": 0,
			"children": []
		}]
	},
	"resources": {
		"rows": [{
			"id": 15,
			"name": "Doris Cole",
			"calendar": "resource_15"
		}, {
			"id": 19,
			"name": "Eli Lambert",
			"calendar": "resource_19"
		}, {
			"id": 18,
			"name": "Ernest Reed",
			"calendar": "resource_18"
		}]
	},
	"assignments": {
		"rows": [{
			"id": 133,
			"resource": 15,
			"event": 77,
			"units": 100
		}, {
			"id": 134,
			"resource": 19,
			"event": 77,
			"units": 100
		}, {
			"id": 135,
			"resource": 18,
			"event": 77,
			"units": 100
		}]
	},
	"calendars": {
		"rows": [{
			"id": 2,
			"name": "Evening shift",
			"hoursPerDay": 4.0,
			"daysPerWeek": 5,
			"daysPerMonth": 20,
			"unspecifiedTimeIsWorking": false,
			"intervals": [{
				"recurrentStartDate": "on monday at 18:00",
				"recurrentEndDate": "on monday at 22:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on tuesday at 18:00",
				"recurrentEndDate": "on tuesday at 22:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on wednesday at 18:00",
				"recurrentEndDate": "on wednesday at 22:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on thursday at 18:00",
				"recurrentEndDate": "on thursday at 22:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on friday at 18:00",
				"recurrentEndDate": "on friday at 22:00",
				"isWorking": true
			}],
			"children": [{
				"id": "resource_15",
				"name": "Doris Cole",
				"intervals": []
			}]
		}, {
			"id": 1,
			"name": "Standard 40 Hours/Week",
			"hoursPerDay": 8.0,
			"daysPerWeek": 5,
			"daysPerMonth": 20,
			"unspecifiedTimeIsWorking": false,
			"intervals": [{
				"recurrentStartDate": "on monday at 8:00",
				"recurrentEndDate": "on monday at 12:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on monday at 13:00",
				"recurrentEndDate": "on monday at 17:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on tuesday at 8:00",
				"recurrentEndDate": "on tuesday at 12:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on tuesday at 13:00",
				"recurrentEndDate": "on tuesday at 17:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on wednesday at 8:00",
				"recurrentEndDate": "on wednesday at 12:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on wednesday at 13:00",
				"recurrentEndDate": "on wednesday at 17:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on thursday at 8:00",
				"recurrentEndDate": "on thursday at 12:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on thursday at 13:00",
				"recurrentEndDate": "on thursday at 17:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on friday at 8:00",
				"recurrentEndDate": "on friday at 12:00",
				"isWorking": true
			}, {
				"recurrentStartDate": "on friday at 13:00",
				"recurrentEndDate": "on friday at 17:00",
				"isWorking": true
			}, {
				"startDate": "2020-05-03 00:00:00",
				"endDate": "2020-05-04 00:00:00",
				"isWorking": false
			}],
			"children": [{
				"id": "resource_19",
				"name": "Eli Lambert",
				"intervals": []
			}, {
				"id": "resource_18",
				"name": "Ernest Reed",
				"intervals": [{
					"startDate": "2020-04-02 00:00:00",
					"endDate": "2020-04-04 00:00:00",
					"isWorking": false
				}]
			}]
		}]
	}
}
I'm aware there might be conflicting values in the Task. I tried to provide the best default values I could.

Then I edit the task and for some reason I have to switch the scheduling mode to normal then back to Fixed Effort, and I have to set all the assignments to 100%. Then for the scheduling to work, I must set the calendar with the earliest interval ("standard 40hrs"). Then, when I change the effort to 12 hours, the result is this:

Start time is 8:00 AM, which is fine as the earliest an employee can start to work is 8:00 AM.
End time is 3:00 PM on the same day, which is wrong. This is 6 working hours later, which means the scheduler thinks there are two employees working from 8:00 AM, but one of the employee (Ernest Reed) has a leave that covers the first two days! The task should have ended at 10:00 PM during the evening shift!

So I'm a bit lost. I hope this is clear enough, if not I can try to work a better example. Any advice?

EDIT: the data was updated to set the task scheduling mode properly, and to include an evening schedule calendar.
Last edited by Jerther on Thu May 14, 2020 8:12 pm, edited 3 times in total.

Post by nickolay »

Hi,

Can we go through it step by step please. If I load your dataset in the advanced example I see that task starts at 04/02 00:00, and ends 04/03 00:00. Since it uses FixedEffort it has adjusted the assignments units to 150%, so that `effort = duration * units` condition holds. The percentage is always the same for all assigned resources and since the 3rd resource has a leave on Apr 02, the remaining 2 resources has to work for 150%, so that in total there's 300% assignment. Note also, that task is actually scheduled on 24/7 default calendar, because neither task itself nor project has the calendar assigned (you may want to assign your default calendar to the project so that all tasks use it by default).

Hope, this is a clear start, where do we take it from here?

Post by nickolay »

Selection_020.png
Selection_020.png (42.33 KiB) Viewed 4358 times
Just noticed that it seems calendars for resources are not loaded for some reason, only the top level calendar, checking why.

Post by nickolay »

Actually the calendars seems to be loaded, its just the `count` property does not reflect the right number, because the top level node is not expanded. Setting "expanded" : true" for standard week calendar fixes that.

I also realized that 2 resources contribute 24h of effort (8 * 150% + 8 * 150%), whereas the event's effort is 72h. This might be a bug, checking that now.

Post by nickolay »

Probably it is related to your calendars report from another thread.

Post by Jerther »

Thank you very much Nickolay

Based on your explanations, I realize the proper scheduling mode I'm looking for is Fixed Units with Effort Driven enabled. I still have to toggle something in the advanced tab for the scheduling to work properly. I made a screencast showing that.

Next, about the task calendar. I just realized the data I included in my original post did not iclude the evening schedule so the interpretation I described did not fully make sense with it. I'm sorry. I corrected the original post. The data now has the correct task scheduling mode and the two calendars.

Also, here is a screencast illustrating the issue I have with setting a calendar on a task. I added some TimeFields in the editor to better view what's going on.
  1. I edit the task and toggle the effort driven checkbox so scheduling works, as previously described
  2. Then I set the effort to 12 hours. The task then lasts 6 hours from midnight and that corresponds to the default calendar's availability for the task's resources minus the one on a leave. That's fine, although it does not consider the fact that none of the resouces work at midnight.
  3. Then I set the task calendar to the standard 40 hours one. Now the task starts at 8, but ends at 15. That's 6 hours, and I don't know what's happening.
  4. Then I set the task calendar to the evening shift and the task moves to 18:00 and ends the next day at 20:00, like only the night shift is working and that would mean 6 working hours for a 12 hours task but there's only one resource on the evening shift. Makes no sens.
The last two elements might be agravated by the bug I've reported but still, if I have to set a calendar on the task, then there is no single calendar that will work because as illustrated in my example, one task may have resources across multiple calendars.

Now, the calendar structure I came up with is not final in any way and could be better adapted.

Post by nickolay »

You are welcome. Quick report is that calendars loading problem is fixed, I've also found few edge cases in fixed effort driven scheduling mode and currently fixing those (half way through). Will get back to you immediately after that.

Post by nickolay »

Ok, reproduced your issues. I checked and most of them are already fixed in the upcoming Gantt 3.0. I'll be backporting the code from it to Gantt 2.0, that will take couple of days.

Some notes:
- Need to specify the `"unspecifiedTimeIsWorking": false,` in every child calendar, this field is not inherited and default value is `true`. This gives unexpected results, since child calendar becomes all working by default. Added a bold note about this to the calendars guide.
- All tasks uses the project calendar for duration convertion. By default project calendar is 24/7 - 24 hours in a day, 7 days in a week. This might give unexpected results as well, when converting a task from business calendar, which usually assumes 8 hours in day, 5 days in a week, for example an 8 hours task will have duration 0.33d. It is recommended to always specify the project calendar. Added a note to the guide as well.

Post by Jerther »

Great!

About the notes:
- I guess this will work fine when the calendars correctly inherit intervals. For now if I set a child calendar without any interval (like id resource_19 in my sample dats) to "unspecifiedTimeIsWorking": false, and assign the resource with this calendar to a task, then the task always has invalid dates.
- The guide describes the calendars configuration well but is shy on what it actually does. Could you describe what the project calendar is used for exactly, compared to resources calendars, and compared to the calendar set on a task? For now I understand the project calendar is used to highlight non-working days in the diagram, and to convert hours to days for the task's duration field. The scheduling engine uses the task calendar to set the starting date of the task, and then resources calendars to compute the duration and end date of the task. Is that right? Is that explained in another part of the documentation?

Post by nickolay »

Regarding the `unspecifiedTimeIsWorking` - still need to have it set as `false` in child calendars as well. If you set it to false and get incorrect results, that is some other bug. Lets return to this one after the other issues will be resolved.

Project calendar is used as a "fallback" calendar in case some entity (task/resource) does not have a calendar assigned. It is also used for duration conversion across the whole project.
The scheduling engine uses the task calendar to set the starting date of the task, and then resources calendars to compute the duration and end date of the task. Is that right?
This is correct. The duration of the task is increased only by the intervals, which are "working" in the task's own calendar and in the calendar of at least one assigned resource.

Post Reply