ProjectModel
Scheduler Pro Project model class - a central place for all data.
It holds and links the stores usually used by Scheduler Pro:
- EventStore
- ResourceStore
- AssignmentStore
- DependencyStore
- CalendarManagerStore
- ResourceTimeRangeStore
- TimeRangeStore
The project uses a scheduling engine to calculate dates, durations and such. It is also responsible for handling references between models, for example to link an event via an assignment to a resource. These operations are asynchronous, a fact that is hidden when working in the Scheduler Pro UI but which you must know about when performing operations on the data level.
When there is a change to data that requires something else to be recalculated, the project schedules a calculation (a commit) which happens moments later. It is also possible to trigger these calculations directly. This flow illustrates the process:
- Something changes which requires the project to recalculate, for example adding a new task:
const [event] = project.eventStore.add({ startDate, endDate });
- A recalculation is scheduled, thus:
event.duration; // <- Not yet calculated
- Calculate now instead of waiting for the scheduled calculation
await project.commitAsync();
event.duration; // <- Now available
Please refer to this guide for more information.
Built-in CrudManager
Scheduler Pro's project has a CrudManager built-in. Using it is the recommended way of syncing data between Scheduler Pro and a backend. Example usage:
const scheduler = new SchedulerPro({
project : {
// Configure urls used by the built-in CrudManager
transport : {
load : {
url : 'php/load.php'
},
sync : {
url : 'php/sync.php'
}
}
}
});
// Load data from the backend
scheduler.project.load()
URLs may also be specified using shortcut configs:
const scheduler = new SchedulerPro({
project : {
loadUrl : 'php/load.php'
syncUrl : 'php/sync.php'
}
});
scheduler.project.load()
For more information on CrudManager, see Schedulers docs on CrudManager. For a detailed description of the protocol used by CrudManager, see the Crud manager guide
You can access the current Project data changes anytime using the changes property.
Working with inline data
The project provides an inlineData getter/setter that can be used to manage data from all Project stores at once. Populating the stores this way can be useful if you do not want to use the CrudManager for server communication but instead load data using Axios or similar.
Getting data
const data = scheduler.project.inlineData;
// use the data in your application
You can read the inline data by console.log(scheduler.project.inlineData);:
{
"calendars": [],
"events": [
{
"id": 1,
"startDate": "2020-03-23T07:00:00+05:00",
},
{
"id": 2,
"startDate": "2020-03-23T11:00:00+05:00",
}
],
"resources": [
{
"id": 1,
"parentId": null,
"name": "George",
"eventColor": "blue",
},
{
"id": 2,
"parentId": null,
"name": "Rob",
"eventColor": "blue",
}
],
"assignments": [
{ id : 1, resource : 1, event : 1 },
{ id : 2, resource : 2, event : 1 },
{ id : 3, resource : 1, event : 2 },
],
"dependencies": [
{
"id": 1,
"type": 2,
"cls": "",
"lag": 0,
"lagUnit": "day",
"fromEvent": 1,
"toEvent": 2,
"active": true
}
],
"timeRanges": [],
"resourceTimeRanges": [],
"project": {
"expanded": true,
"hoursPerDay": 24,
"daysPerWeek": 7
}
}
xxData properties are deprecated and will be removed in the future. Use xx instead. For
example eventsData is deprecated, use events instead. For now, both naming schemes are included in
the data object
Setting data
// Get data from server manually
const data = await axios.get('/project?id=12345');
// Feed it to the project
scheduler.project.inlineData = data;
It should have the following structure:
scheduler.project.loadInlineData = {
resources: [
{
id : 1,
name : 'George',
type : 'Operators',
},
{
id : 2,
name : 'Rob',
type : 'Operators',
},
{
id : 3,
name : 'Mike',
type : 'Main'
},
],
events: [
{
id : 1,
name : 'Meeting',
startDate : '2020-03-23T07:00',
duration : 4,
durationUnit : 'hour',
percentDone : 100,
},
{
id : 2,
startDate : '2020-03-23T11:00',
name : 'Get parts',
durationUnit : 'hour',
duration : 3,
percentDone : 40,
},
],
assignment : [
{ id : 1, resource : 1, event : 1 },
{ id : 2, resource : 2, event : 1 },
{ id : 3, resource : 2, event : 2 },
{ id : 4, resource : 3, event : 2 },
],
dependencies: [
{
id : 1,
fromEvent : 1,
toEvent : 2
},
],
};
You can also use loadInlineData() function:
scheduler.project.loadInlineData({
resources : [],
events : [],
dependencies : []
})
Either you can pass the response in this format directly:
{
resource : [],
events : [],
dependencies : []
}
Or you can modify the response once received and pass it to the loadInlineData() function or assign it to
inlineData.
See also loadInlineData
xxData properties are deprecated and will be removed in the future. Use xx instead. For
example eventsData is deprecated, use events instead. For now, both naming schemes are included in
the data object
Getting changed records
You can access the changes in the current Project dataset anytime using the changes property. It returns an object with all changes:
const changes = project.changes;
console.log(changes);
The changes object has the following structure:
{
events : {
updated : [{
name : 'My task',
id : 12
}]
},
assignments : {
added : [{
event : 12,
resource : 7,
units : 100,
$PhantomId : 'abc123'
}]
}
};
Monitoring data changes
While it is possible to listen for data changes on the projects individual stores, it is sometimes more convenient to have a centralized place to handle all data changes. By listening for the change event your code gets notified when data in any of the stores changes. Useful for example to keep an external data model up to date:
const scheduler = new SchedulerPro({
project: {
listeners : {
change({ store, action, records }) {
const { $name } = store.constructor;
if (action === 'add') {
externalDataModel.add($name, records);
}
if (action === 'remove') {
externalDataModel.remove($name, records);
}
}
}
}
});
Processing the data loaded from the server
If you want to process the data received from the server after loading, you can use the beforeLoadApply or beforeSyncApply events:
const gantt = new Gantt({
project: {
listeners : {
beforeLoadApply({ response }) {
// do something with load-response object before data is fed to the stores
}
}
}
});
Built-in StateTrackingManager
The project also has a built-in StateTrackingManager (STM for short), that handles undo/redo for the project stores (additional stores can also be added). By default, it is only used while editing tasks using the task editor, the editor updates tasks live and uses STM to rollback changes if canceled. But you can enable it to track all project store changes:
// Enable automatic transaction creation and start recording
project.stm.autoRecord = true;
project.stm.enable();
// Undo a transaction
project.stm.undo();
// Redo
project.stm.redo();
Check out the undoredo demo to see it in action.
Configs
54
Configs
54Advanced
Enables early rendering in SchedulerPro, by postponing calculations to after the first refresh.
Requires event data loaded to be pre-normalized to function as intended, since it will be used to render before engine has normalized the data. Given un-normalized data events will snap into place when calculations are finished.
The Gantt chart will be read-only until the initial calculations are finished.
Set to true to enable calculation progress notifications.
When enabled the project fires progress event.
Note: Enabling progress notifications will impact calculation performance, since it needs to pause calculations to allow redrawing the UI.
Maximum range the project calendars can iterate.
The value is defined in milliseconds and by default equals 5 years roughly.
new SchedulerPro({
project : {
// adjust calendar iteration limit to 10 years roughly:
// 10 years expressed in ms
maxCalendarRange : 10 * 365 * 24 * 3600000,
...
}
});
Setting this to true (default) vetoes silenceInitialCommit effect
by preventing silent changes accepting
on initial data loading if some scheduling issue gets resolved during it.
Enabling this config allows an application to persist the data in a resolved state.
Set to true to reset the undo/redo queues of the internal StateTrackingManager
after the Project has loaded. Defaults to false
Silences propagations caused by the project loading.
Applying the loaded data to the project occurs in two basic stages:
- Data gets into the engine graph which triggers changes propagation
- The changes caused by the propagation get written to related stores
Setting this flag to true makes the component perform step 2 silently without triggering events causing reactions on those changes
(like sending changes back to the server if autoSync is enabled) and keeping stores in unmodified state.
This is safe if the loaded data is consistent so propagation doesn't really do any adjustments.
By default the system treats the data as consistent so this option is true.
new SchedulerPro{
project : {
// We want scheduling engine to recalculate the data properly
// so then we could save it back to the server
silenceInitialCommit : false,
...
}
...
})
Inline data
Data use to fill the assignmentStore. Should be an array of AssignmentModels or its configuration objects.
Data use to fill the eventStore. Should be a CalendarModel array or its configuration objects.
Data use to fill the dependencyStore. Should be an array of DependencyModels or its configuration objects.
Data use to fill the eventStore. Should be an array of EventModels or its configuration objects.
Data use to fill the resourceStore. Should be an array of ResourceModels or its configuration objects.
Data use to fill the resourceTimeRangeStore. Should be an array of ResourceTimeRangeModels or its configuration objects.
Data use to fill the timeRangeStore. Should be an array of TimeSpans or its configuration objects.
Legacy inline data
The initial data, to fill the assignmentStore with. Should be an array of AssignmentModels or its configuration objects.
The initial data, to fill the calendarManagerStore with. Should be an array of CalendarModel or it's configuration objects.
The initial data, to fill the dependencyStore with. Should be an array of DependencyModels or its configuration objects.
The initial data, to fill the eventStore with. Should be an array of EventModels or its configuration objects.
The initial data, to fill the resourceStore with. Should be an array of ResourceModels or its configuration objects.
Models & Stores
The constructor of the assignment model class, to be used in the project. Will be set as the modelClass property of the assignmentStore
An AssignmentStore instance or a config object.
The constructor to create an assignment store instance with. Should be a class, subclassing the AssignmentStore
A CalendarManagerStore instance or a config object.
The constructor to create a calendar store instance with. Should be a class, subclassing the CalendarManagerStore
The constructor of the calendar model class, to be used in the project. Will be set as the modelClass property of the calendarManagerStore
The constructor of the dependency model class, to be used in the project. Will be set as the modelClass property of the dependencyStore
A DependencyStore instance or a config object.
The constructor to create a dependency store instance with. Should be a class, subclassing the DependencyStore
The constructor of the event model class, to be used in the project. Will be set as the modelClass property of the eventStore
An EventStore instance or a config object.
The constructor to create an event store instance with. Should be a class, subclassing the EventStore
Class implementing resource allocation report used by resource histogram and resource utilization views for collecting resource allocation.
The constructor of the resource model class, to be used in the project. Will be set as the modelClass property of the resourceStore
A ResourceStore instance or a config object.
The constructor to create a resource store instance with. Should be a class, subclassing the ResourceStore
Other
This config manages DST correction in the scheduling engine. It only has effect when DST transition hour is working time. Usually a DST transition occurs on a Sunday, so with non-working weekends the DST correction logic is not involved.
If true, it will add/remove one hour when calculating end date. For example: Assume weekends are working and on Sunday, 2020-10-25 at 03:00 clocks are set back 1 hour. Assume there is an event:
{
startDate : '2020-10-20',
duration : 10 * 24 + 1,
durationUnit : 'hour'
}
It will end on 2020-10-30 01:00 (which is wrong) but duration will be reported correctly. Because of the DST transition the SchedulerPro project will add one more hour when calculating the end date.
Also this may occur when day with DST transition is working but there are non-working intervals between that day and event end date.
{
calendar : 1,
calendars : [
{
id : 1,
startDate : '2020-10-26',
endDate : '2020-10-27',
isWorking : false
}
],
events : [
{
id : 1,
startDate : '2020-10-20',
endDate : '2020-10-30'
},
{
id : 2,
startDate : '2020-10-20',
duration : 10 * 24 + 1,
durationUnit : 'hour'
}
]
}
Event 1 duration will be incorrectly reported as 9 days * 24 hours, missing 1 extra hour added by DST transition. Event 2 end date will be calculated to 2020-10-30 01:00, adding one extra hour.
If false, the SchedulerPro project will not add DST correction which fixes the quirk mentioned above. Event 1 duration will be correctly reported as 9 days * 24 hours + 1 hour. Event 2 end date will be calculated to 2020-10-30.
Also, for those events days duration will be a floating point number due to extra (or missing) hour:
eventStore.getById(1).getDuration('day') // 10.041666666666666
eventStore.getById(1).getDuration('hour') // 241
CRUD
Properties
37
Properties
37Advanced
Enables/disables the calculation progress notifications.
Class hierarchy
Inline data
Get/set assignmentStore data.
Always returns an array of AssignmentModels but also accepts an array of its configuration objects as input.
Get/set calendarManagerStore data.
Always returns a CalendarModel array but also accepts an array of its configuration objects as input.
Get/set dependencyStore data.
Always returns an array of DependencyModels but also accepts an array of its configuration objects as input.
Get/set eventStore data.
Always returns an array of EventModels but also accepts an array of its configuration objects as input.
Get/set resourceStore data.
Always returns an array of ResourceModels but also accepts an array of its configuration objects as input.
Get/set resourceTimeRangeStore data.
Always returns an array of ResourceTimeRangeModels but also accepts an array of its configuration objects as input.
Get/set timeRangeStore data.
Always returns an array of TimeSpans but also accepts an array of its configuration objects as input.
Models & Stores
The store holding the assignment information.
See also AssignmentModel
The store holding the calendar information.
See also CalendarModel
Returns current Project changes as an object consisting of added/modified/removed arrays of records for every
managed store. Returns null if no changes exist. Format:
{
resources : {
added : [{ name : 'New guy' }],
modified : [{ id : 2, name : 'Mike' }],
removed : [{ id : 3 }]
},
events : {
modified : [{ id : 12, name : 'Cool task' }]
},
...
}
The store holding the dependency information.
See also DependencyModel
The store holding the event information.
See also EventModel
The store holding the resources that can be assigned to the events in the event store.
See also ResourceModel
Other
Class implementing an event segment. Can be used for customizing the class implementing segments:
// new class for a segment
class MySegment extends EventSegmentModel {
static fields = [
{ name : 'responsiblePerson' }
];
}
new SchedulerPro({
project : {
// tell the project to use the new class for segments
segmentModelClass : MySegment
}
})
CRUD
Legacy inline data
Misc
Functions
16
Functions
16Common
Project changes (CRUD operations to records in its stores) are automatically committed on a buffer to the underlying graph based calculation engine. The engine performs it calculations async.
By calling this function, the commit happens right away. And by awaiting it you are sure that project calculations are finished and that references between records are up to date.
The returned promise is resolved with an object. If that object has rejectedWith set, there has been a conflict and the calculation failed.
// Move an event in time
eventStore.first.shift(1);
// Trigger calculations directly and wait for them to finish
const result = await project.commitAsync();
if (result.rejectedWith) {
// there was a conflict during the scheduling
}
Inline data
Accepts a "data package" consisting of data for the projects stores, which is then loaded into the stores.
The package can hold data for EventStore, AssignmentStore, ResourceStore, DependencyStore and Calendar Manager. It uses the same format as when creating a project with inline data:
await project.loadInlineData({
events : [
{ id : 1, name : 'Proof-read docs', startDate : '2017-01-02', endDate : '2017-01-09' },
{ id : 2, name : 'Release docs', startDate : '2017-01-09', endDate : '2017-01-10' }
],
resources : [
{ id : 1, name : 'Arcady' },
{ id : 2, name : 'Don' }
],
dependencies : [
{ fromEvent : 1, toEvent : 2 }
],
assignments : [
{ 'event' : 1, 'resource' : 1 },
{ 'event' : 2, 'resource' : 2 }
]
calendars : [
{
id : 111,
name : 'My cool calendar',
intervals : [
{
recurrentStartDate : 'on Sat',
recurrentEndDate : 'on Mon',
isWorking : false
}
]
}
]
});
xxData properties are deprecated and will be removed in the future. Use xx instead. For
example eventsData is deprecated, use events instead. For now, both naming schemes are included in
the data object
After populating the stores it commits the project, starting its calculations. By awaiting loadInlineData() you
can be sure that project calculations are finished.
| Parameter | Type | Description |
|---|---|---|
dataPackage | Object | A data package as described above |
Other
Revisions
Events
10
Events
10Fired when the Engine detects a computation cycle.
// Adding a listener using the "on" method
projectModel.on('cycle', ({ schedulingIssue, schedulingIssue.getDescription, schedulingIssue.cycle, schedulingIssue.getResolutions, continueWithResolutionResult }) => {
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | Scheduling error describing the case: |
schedulingIssue.getDescription | function | Returns the cycle description |
schedulingIssue.cycle | Object | Object providing the cycle info |
schedulingIssue.getResolutions | function | Returns possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations:
Where |
Fired when the engine has finished its calculations and the results has been written back to the records.
scheduler.project.on({
dataReady() {
console.log('Calculations finished');
}
});
scheduler.eventStore.first.duration = 10;
// At some point a bit later it will log 'Calculations finished'
// Adding a listener using the "on" method
projectModel.on('dataReady', ({ source, isInitialCommit, records }) => {
});| Parameter | Type | Description |
|---|---|---|
source | ProjectModel | The project |
isInitialCommit | Boolean | Flag that shows if this commit is initial |
records | Set | Set of all Models that were modified in the completed transaction. Use the modifications property of each Model to identify modified fields. |
Fired when the Engine detects a calendar misconfiguration when the calendar does not provide any working periods of time which makes the calendar usage impossible.
// Adding a listener using the "on" method
projectModel.on('emptyCalendar', ({ schedulingIssue, schedulingIssue.getDescription, schedulingIssue.getCalendar, schedulingIssue.getResolutions, continueWithResolutionResult }) => {
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | Scheduling error describing the case: |
schedulingIssue.getDescription | function | Returns the error description |
schedulingIssue.getCalendar | function | Returns the calendar that must be fixed |
schedulingIssue.getResolutions | function | Returns possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations: |
Fired during the Engine calculation if enableProgressNotifications config is true
// Adding a listener using the "on" method
projectModel.on('progress', ({ source, total, remaining, phase }) => {
});| Parameter | Type | Description |
|---|---|---|
source | ProjectModel | The owning project |
total | Number | The total number of operations |
remaining | Number | The number of remaining operations |
phase | storePopulation | propagating | The phase of the calculation, either 'storePopulation' when data is getting loaded, or 'propagating' when data is getting calculated |
Fired when the Engine detects a scheduling conflict.
// Adding a listener using the "on" method
projectModel.on('schedulingConflict', ({ schedulingIssue, schedulingIssue.getDescription, schedulingIssue.intervals, schedulingIssue.getResolutions, continueWithResolutionResult }) => {
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | The conflict details: |
schedulingIssue.getDescription | function | Returns the conflict description |
schedulingIssue.intervals | Object[] | Array of conflicting intervals |
schedulingIssue.getResolutions | function | Function to get possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations:
Where |
Event handlers
10
Event handlers
10Called when the Engine detects a computation cycle.
new ProjectModel({
onCycle({ schedulingIssue, continueWithResolutionResult }) {
}
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | Scheduling error describing the case: |
schedulingIssue.getDescription | function | Returns the cycle description |
schedulingIssue.cycle | Object | Object providing the cycle info |
schedulingIssue.getResolutions | function | Returns possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations:
Where |
Called when the engine has finished its calculations and the results has been written back to the records.
scheduler.project.on({
dataReady() {
console.log('Calculations finished');
}
});
scheduler.eventStore.first.duration = 10;
// At some point a bit later it will log 'Calculations finished'
new ProjectModel({
onDataReady({ source, isInitialCommit, records }) {
}
});| Parameter | Type | Description |
|---|---|---|
source | ProjectModel | The project |
isInitialCommit | Boolean | Flag that shows if this commit is initial |
records | Set | Set of all Models that were modified in the completed transaction. Use the modifications property of each Model to identify modified fields. |
Called when the Engine detects a calendar misconfiguration when the calendar does not provide any working periods of time which makes the calendar usage impossible.
new ProjectModel({
onEmptyCalendar({ schedulingIssue, continueWithResolutionResult }) {
}
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | Scheduling error describing the case: |
schedulingIssue.getDescription | function | Returns the error description |
schedulingIssue.getCalendar | function | Returns the calendar that must be fixed |
schedulingIssue.getResolutions | function | Returns possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations: |
Called during the Engine calculation if enableProgressNotifications config is true
new ProjectModel({
onProgress({ source, total, remaining, phase }) {
}
});| Parameter | Type | Description |
|---|---|---|
source | ProjectModel | The owning project |
total | Number | The total number of operations |
remaining | Number | The number of remaining operations |
phase | storePopulation | propagating | The phase of the calculation, either 'storePopulation' when data is getting loaded, or 'propagating' when data is getting calculated |
Called when the Engine detects a scheduling conflict.
new ProjectModel({
onSchedulingConflict({ schedulingIssue, continueWithResolutionResult }) {
}
});| Parameter | Type | Description |
|---|---|---|
schedulingIssue | Object | The conflict details: |
schedulingIssue.getDescription | function | Returns the conflict description |
schedulingIssue.intervals | Object[] | Array of conflicting intervals |
schedulingIssue.getResolutions | function | Function to get possible resolutions |
continueWithResolutionResult | function | Function to call after a resolution is chosen to proceed with the Engine calculations:
Where |
Typedefs
2
Typedefs
2Fields
10
Fields
10If this flag is set to true (default) when a start/end date is set on the event, a corresponding
start-no-earlier/later-than constraint is added, automatically. This is done in order to
keep the event "attached" to this date, according to the user intention.
Depending on your use case, you might want to disable this behaviour.
true to enable automatic % done calculation for summary
tasks, false to disable it.
When true (default) adjacent or overlapping event segments get merged automatically.
The project calendar.
The number of days per month.
Please note: the value does not define the amount of working time per month for that purpose one should use calendars.
The value is used when converting the duration from one unit to another.
So when user enters a duration of, for example, 1 month the system understands that it
actually means 30 days (which is then converted to hours) and
schedules accordingly.
The number of days per week.
Please note: the value does not define the amount of working time per week for that purpose one should use calendars.
The value is used when converting the duration from one unit to another.
So when user enters a duration of, for example, 2 weeks the system understands that it
actually means 14 days (which is then converted to hours) and
schedules accordingly.
The source of the calendar for dependencies (the calendar used for taking dependencies lag into account). Possible values are:
ToEvent- successor calendar will be used (default);FromEvent- predecessor calendar will be used;Project- the project calendar will be used.
The number of hours per day.
Please note: the value does not define the amount of working time per day for that purpose one should use calendars.
The value is used when converting the duration from one unit to another.
So when user enters a duration of, for example, 5 days the system understands that it
actually means 120 hours and schedules accordingly.
When true the project's manually scheduled tasks duration will include only working periods of time.
When false such tasks will ignore working time calendars and treat all intervals as working.
IMPORTANT: Setting this to false also forcefully sets the
skipNonWorkingTimeWhenSchedulingManually option to false.
When true the project's manually scheduled tasks will adjust their proposed start/end dates to skip non-working
time.