Optimize

Find a valid scheduling solution for multiple jobs and resources.

You can use Optimize to find a valid solution to scheduling multiple jobs across your resources during a scheduling window.

The Optimize feature allows you to specify the scheduling window, the jobs you want to optimize, and optimization options or constraints.

The /planr/optimize/schedule REST API endpoint requires the following fields in the request body:

Field Type Description
jobIds Array of string An array of job IDs for which you want to schedule and allocate resources.
resourceIds Array of string An array of resource IDs that can be allocated and scheduled for the jobs.
scheduleStart String <date-time> (TimeStamp) Can be either ISO8601 date time string or Unix timestamp number ( in milliseconds ). Always returned in ISO8601 format.
scheduleEnd String <date-time> (TimeStamp) Can be either ISO8601 date time string or Unix timestamp number ( in milliseconds ). Always returned in ISO8601 format.
timeZone String Scheduling timezone.
schedulingOptions object(SchedulingOptions) Constraints that the optimization engine must consider when creating the schedule.

Optimization schedulingOptions are a way to configure the optimization engine to take specific business requirements into account, adding further constraints to your query.

The following schedulingOptions are available:

Field Description Type Default
balanceWorkload Equalize total allocated duration including travel time for each resource. Boolean false
minimizeResources Attempt to allocate to the fewest number of resources in a scheduling window. Boolean false
jobTimeAsTimeConstraint When a job has a time set, consider it to be a time constraint and never reschedule to another time slot. Boolean true
preferJobTimeOverTimeConstraint When a job with a JobTimeConstraint also has a time set, consider the job time as the authoritative constraint and ignore the JobTimeConstraint. Boolean true
respectSchedule For a job that is allocated and in “Pending Dispatch” status, do not move the job to a different time. Boolean true
ignoreTravelTimes Ignore all travel times between resources and jobs. Boolean false
ignoreTravelTimeFirstJob Ignore travel time from a resource’ home location to the first job. Boolean false
ignoreTravelTimeLastJob Ignore travel time to the resource’ home location from the last job. Boolean false
padding A fixed interval in seconds between consecutive jobs. Integer 0
snapUnit Divide the given time frame into parts described by this value and allocate all jobs to the nearest part in seconds. Integer 0 (>=0)

Request body schemas for /optimize/schedule

The /optimize/schedule endpoint has two different ways of specifying the resources you want to include in the request: * The resources object contains the IDs of the resources you want to schedule for jobs, including their availability window. This is useful when making the request from an external system where the resource record might not be available. * The resourceIds property is an array of resource IDs that can be scheduled for jobs.

Example request body using the resources object

The resources object that contains the resource IDs and an array of start and end timestamps for the working hours of those resources.

Working hours override availability that is already set up for the resource, so this is a way to account for a varied or one-off changes to a resource’s normal working hours.

For example, if a resource normally only works from 9AM to 5PM during the week but has elected to be available for overtime shifts this weekend, this can be included as working hours.

The following is an example of a request for the /optimize/schedule endpoint using the resources object:

{
  "jobIds": [
      "001406d4-51fd-4da7-8960-1e71429e1974",
      "0014c5a0-05eb-4d16-8381-f63d17bda9d6",
      "00142eaa-a8f3-4e90-942b-3233fff67936"
  ],
  "resources": {
      "0005dfa7-c49e-481c-a25c-6daef8e3d918": [],
      "0005094d-dae5-4cbb-ad7d-db78d3735e21": [{ "start": "2019-04-17T04:00:30.000Z", "end": "2019-04-17T0:20:30.000Z" }]
  },
  "scheduleStart": 1555475530486,
  "scheduleEnd": 1555509599999,
  "timeZone": "Australia/Brisbane",
  "schedulingOptions": {
      "respectSchedule": false,
      "ignoreTravelTimes": true,
      "ignoreTravelTimeFirstJob": false,
      "ignoreTravelTimeLastJob": false,
      "jobTimeAsTimeConstraint": true,
      "preferJobTimeOverTimeConstraint": true,
      "balanceWorkload": false,
      "minimizeResources": false,
      "snapUnit": 0,
      "padding": 0
  }
}

Example request body using the resourceIds property

The resourceId property is an array of resource IDs that can be scheduled for the jobs included in the request.

The following is an example of an /optimize/schedule request using the resourceIds property:

{
  "jobIds": [
      "001406d4-51fd-4da7-8960-1e71429e1974",
      "0014c5a0-05eb-4d16-8381-f63d17bda9d6",
      "00142eaa-a8f3-4e90-942b-3233fff67936"
  ],
  "resourceIds": [
      "0005dfa7-c49e-481c-a25c-6daef8e3d918"
      "000577e0-2d69-4e84-a1c3-1878d64f85f0"
  ],
  "scheduleStart": 1555475530486,
  "scheduleEnd": 1555509599999,
  "timeZone": "Australia/Brisbane",
  "schedulingOptions": {
      "respectSchedule": false,
      "ignoreTravelTimes": true,
      "ignoreTravelTimeFirstJob": false,
      "ignoreTravelTimeLastJob": false,
      "jobTimeAsTimeConstraint": true,
      "preferJobTimeOverTimeConstraint": true,
      "balanceWorkload": false,
      "minimizeResources": false,
      "snapUnit": 0,
      "padding": 0
  }
}

Example: Get a valid schedule using the /optimize/schedule endpoint

Use the following procedure to make a request to the /optimize/schedule endpoint and get a valid schedule using the ScheduleResourceIdsBody request schema.

The example includes 10 jobs that we want to schedule on Monday. There are three resources that we are including in the query, and we would like the schedule to use minimum resources (minimizeResources):

  1. Create a ScheduleResourceIdsBody request body including the jobIds and resourceIds for the jobs and resources that you want to include in your optimization query, and the scheduling window.

    {
    "jobIds": [
        "00146e2f-c8db-4593-adc2-084abc866785","0014586b-3fa3-4f74-a5b1-a47f22a4b3a2","0014b2b8-6543-4d3a-b39f-91d9ccadb12f","001419d8-ea58-40d8-8fc9-7e5e114af0e0","00144739-bd24-462b-8f6a-ff01a3c80671","00143563-d261-4a7d-9416-2244447250c2","00140dcd-b523-4250-921b-4eb32d29d043","00148397-e956-4232-854a-ed5c6f91455e","00141ae5-a30e-44d9-a24b-a04ed98c24f9","0014e1ba-eddd-447c-ae80-62954c637820"
        ],
        "resourceIds": [
        "000521e3-b4ab-417e-8fc2-23647246b085", "0005a7e9-b1aa-44da-937f-310b921b75cc", "000520bd-63a1-47a0-9c80-9a343cb35ec6"
        ],
        "scheduleStart": "2019-07-15T00:00:00+1000",
        "scheduleEnd": 	"2019-07-15T23:59:59+1000",
        "timeZone": "Australia/Brisbane",
        "schedulingOptions": {
          "respectSchedule": false,
          "ignoreTravelTimes": false,
          "ignoreTravelTimeFirstJob": true,
          "ignoreTravelTimeLastJob": true,
          "jobTimeAsTimeConstraint": true,
          "preferJobTimeOverTimeConstraint": true,
          "balanceWorkload": false,
          "minimizeResources": true,
          "snapUnit": 0,
          "padding": 5
        }
      }

    This provides the following JSON response, which is a result that assigns all 10 jobs to two of the three resources:

    {
      "result": {
        "score": {
          "hardScore": 0,
          "mediumScore": 0,
          "softScore": -10000
        },
        "timeToSolve": 2023,
        "routes": [
          {
            "resourceId": "000520bd-63a1-47a0-9c80-9a343cb35ec6",
            "resourceName": "David Kimi",
            "route": [
              {
                "jobId": "00140dcd-b523-4250-921b-4eb32d29d043",
                "jobName": "JOB-0028",
                "start": "2019-07-15T03:29:00.000Z",
                "duration": 50,
                "travelTime": 29,
                "type": "job"
              },
              {
                "jobId": "001419d8-ea58-40d8-8fc9-7e5e114af0e0",
                "jobName": "JOB-0003",
                "start": "2019-07-14T22:30:00.000Z",
                "duration": 10,
                "travelTime": 0,
                "type": "job"
              },
              {
                "jobId": "0014586b-3fa3-4f74-a5b1-a47f22a4b3a2",
                "jobName": "JOB-0009",
                "start": "2019-07-14T22:45:00.000Z",
                "duration": 60,
                "travelTime": 0,
                "type": "job"
              },
              {
                "jobId": "00146e2f-c8db-4593-adc2-084abc866785",
                "jobName": "JOB-0006",
                "start": "2019-07-14T23:50:00.000Z",
                "duration": 90,
                "travelTime": 0,
                "type": "job"
              },
              {
                "jobId": "00148397-e956-4232-854a-ed5c6f91455e",
                "jobName": "JOB-0015",
                "start": "2019-07-15T01:25:00.000Z",
                "duration": 90,
                "travelTime": 0,
                "type": "job"
              },
              {
                "jobId": "0014b2b8-6543-4d3a-b39f-91d9ccadb12f",
                "jobName": "JOB-0023",
                "start": "2019-07-15T04:31:00.000Z",
                "duration": 60,
                "travelTime": 7,
                "type": "job"
              },
              {
                "jobId": "0014e1ba-eddd-447c-ae80-62954c637820",
                "jobName": "JOB-0022",
                "start": "2019-07-15T06:23:00.000Z",
                "duration": 60,
                "travelTime": 47,
                "type": "job"
              }
            ]
          },
          {
            "resourceId": "000521e3-b4ab-417e-8fc2-23647246b085",
            "resourceName": "Mary Brown",
            "route": [
              {
                "jobId": "00141ae5-a30e-44d9-a24b-a04ed98c24f9",
                "jobName": "JOB-0016",
                "start": "2019-07-14T23:00:00.000Z",
                "duration": 120,
                "travelTime": 0,
                "type": "job"
              },
              {
                "jobId": "00143563-d261-4a7d-9416-2244447250c2",
                "jobName": "JOB-0014",
                "start": "2019-07-15T02:10:00.000Z",
                "duration": 50,
                "travelTime": 65,
                "type": "job"
              },
              {
                "jobId": "00144739-bd24-462b-8f6a-ff01a3c80671",
                "jobName": "JOB-0008",
                "start": "2019-07-15T03:05:00.000Z",
                "duration": 90,
                "travelTime": 0,
                "type": "job"
              }
            ]
          }
        ],
        "unscheduled": []
        }
      }
  2. You can save these results and dispatch the jobs using a GraphQL mutation:

    • The GraphQL query uses aliases to allocate multiple jobs to resources in a single query.
    • You must provide an End time for the job.

    The following example allocates and dispatches the three jobs that were assigned to Mary Brown:

    mutation saveOptimizationResult {
      schema {
        _j0: updateJobs(input: {
          UID: "00141ae5-a30e-44d9-a24b-a04ed98c24f9"
          Start: "2019-07-14T23:00:00.000Z",
          End: "2019-07-15T01:00:00.000Z"
        })
        _ja0: insertJobAllocations(input: {
          JobId: "00141ae5-a30e-44d9-a24b-a04ed98c24f9"
          ResourceId: "000521e3-b4ab-417e-8fc2-23647246b085"
        })
        _j1: updateJobs(input: {
          UID: "00143563-d261-4a7d-9416-2244447250c2"
          Start: "2019-07-15T02:10:00.000Z"
          End: "2019-07-15T03:00:00.000Z"
        })
        _ja1: insertJobAllocations(input: {
          JobId: "00143563-d261-4a7d-9416-2244447250c2"
          ResourceId: "000521e3-b4ab-417e-8fc2-23647246b085"
        })
        _j2: updateJobs(input: {
          UID: "00144739-bd24-462b-8f6a-ff01a3c80671"
          Start: "2019-07-15T03:05:00.000Z"
          End: "2019-07-15T04:35:00.000Z"
        })
        _ja2: insertJobAllocations(input: {
          JobId: "00144739-bd24-462b-8f6a-ff01a3c80671"
          ResourceId: "000521e3-b4ab-417e-8fc2-23647246b085"
        })
      }
    }

    A successful result provides the following response with IDs for each successful operation:

    {
      "data": {
        "schema": {
          "_j0": "00141ae5-a30e-44d9-a24b-a04ed98c24f9",
          "_ja0": "00186136-fdad-4a2b-82c0-ead73cc6c3a7",
          "_j1": "00143563-d261-4a7d-9416-2244447250c2",
          "_ja1": "00182053-28dd-4e6f-8f8e-69d60fda0755",
          "_j2": "00144739-bd24-462b-8f6a-ff01a3c80671",
          "_ja2": "0018a0cb-cde2-48a7-98bb-e8c10c93b915"
        }
      }
    }

    Open the scheduling window to see that the jobs have been scheduled and allocated to Mary Brown and are Pending Dispatch.

  3. You can dispatch the jobs by selecting them in the scheduling window and clicking Notify in the scheduler. Alternatively, you can dispatch the jobs using GraphQL:

    mutation dispatchJobs {
      schema {
        _j0: updateJobs(input: {
          UID: "00141ae5-a30e-44d9-a24b-a04ed98c24f9"
          JobStatus: "Dispatched"
        })
        _j1: updateJobs(input: {
          UID: "00143563-d261-4a7d-9416-2244447250c2"
          JobStatus: "Dispatched"
        })
        _j2: updateJobs(input: {
          UID: "00144739-bd24-462b-8f6a-ff01a3c80671"
          JobStatus: "Dispatched"
        })
      }
    }

    Or you can dispatch the jobs using the /notify/dispatch endpoint, which also triggers the dispatch workflow:

    {
      "jobId": "00141ae5-a30e-44d9-a24b-a04ed98c24f9",
      "resourceIds": [
        "000521e3-b4ab-417e-8fc2-23647246b085"
      ]
    }