Update Stops from a live Plan
Here we will be using the Update Live Stop, the Re-Optimize Plan, and the Re-Distribute Plan endpoints to update a Stop from a Plan that was already optimized, and therefore not editable via the Update Stop endpoint.
- JS Web
- Node
- Python
- curl
// Set the API key
const apiKey = '<your-api-key>'
// Headers for the request
const headers = new Headers()
headers.append('Content-Type', 'application/json')
headers.append('Authorization', `Basic ${btoa(`${apiKey}:`)}`)
async function updateStop(stopId) {
// example stop data to update
const stopData = {
notes: 'Update notes',
timing: {
earliestAttemptTime: {
hour: 12,
minute: 0,
},
},
}
// We update the stop using the liveUpdate method
const updateStopResponse = await fetch(
`https://api.getcircuit.com/public/v0.2b/${stopId}:liveUpdate`,
{
method: 'POST',
headers: headers,
body: JSON.stringify(stopData),
},
)
// The response will return an object similar to the following:
// {
// "pending": true,
// "stop": {
// "id": "plans/FQ95Ex714KYeojkeIm77/stops/vgsTiQi85ueWRs1JnXx7",
// "address": { ... }
// ...
// },
// ...
// }
const updateStopResponseJson = await updateStopResponse.json()
console.log(updateStopResponseJson)
// Now, we have to check if the update was applied to the plan or if it's
// pending a new optimization and distribution. On `pending = true`, we must
// use the re-optimize and re-distribute endpoints to apply the changes to the plan.
if (updateStopResponseJson.pending) {
// We can get the planId from the endpoint response
const planId = updateStopResponseJson.stop.plan
// Let us re-optimize this plan:
const reoptimizationResponse = await fetch(
`https://api.getcircuit.com/public/v0.2b/${planId}:reoptimize`,
{
method: 'POST',
headers,
body: JSON.stringify({}),
},
)
// The response will return an operation object similar to the following:
// {
// "id": "operations/FQ95Ex714KYeojkeIm77",
// "done": false,
// "type": "plan_optimization",
// ...
// }
//
// Why an operation? Because the re-optimization can take a long while to
// finish, so we process it in an asynchronous manner.
let operation = await reoptimizationResponse.json()
console.log(operation)
// Now we need to wait until the re-optimization is done. As this can take a very
// long while depending on the number of stops, we will check it in a loop
// every few seconds.
//
// If the route is small enough, the re-optimization response itself can already
// return a done operation, the condition on this loop prevents us from
// even start polling if that is the case
//
// IMPORTANT: remember to add a delay between each check so
// as to not be rate-limited.
while (!operation.done) {
// Sleep/wait for a certain duration before the next check
await new Promise((resolve) => setTimeout(resolve, 5000)) // 5 seconds delay
const operationProgress = await fetch(
`https://api.getcircuit.com/public/v0.2b/${operation.id}`,
{
headers,
},
)
operation = await operationProgress.json()
}
// Now the operation is done, which means it finished processing, and it might
// have had a successful result or an error one, let's check for it (the
// result field is not included in the response until the operation finishes):
const result = operation.result
// check for errors: if there is a code and message it means that the
// operation errored with said code and error messages, check the docs for the
// meaning of these
if (result.code && result.message) {
throw new Error(
`Operation failed with ${result.code}: ${result.message}`,
)
}
// otherwise the operation was succesful
console.log(
`Operation successfully optimized ${result.numOptimizedStops} stops`,
)
// but it still might have ignored some stops due to time constraints or other
// problems such as the stop is not reachable:
if (result.skippedStops.length > 0) {
console.log(
`Operation skipped a total of ${result.skippedStops.length} stops: check them manually`,
)
}
// Now finally we can re-distribute this plan and thus apply the changes to the stop that was
// performed before
const redistributeResponse = await fetch(
`https://api.getcircuit.com/public/v0.2b/${planId}:redistribute`,
{
method: 'POST',
headers,
body: JSON.stringify({}),
},
)
// the above will return the plan with the `distributed` attribute set to `true`
}
}
// We are using the axios library here to make requests from Node
const axios = require('axios')
// Set the API key
const apiKey = '<your-api-key>'
// Axios instance with base configuration
const api = axios.create({
baseURL: 'https://api.getcircuit.com/public/v0.2b',
auth: {
username: apiKey,
},
})
async function updateStop(stopId) {
// example stop data to update
const stopData = {
notes: 'Update notes',
timing: {
earliestAttemptTime: {
hour: 12,
minute: 0,
},
},
}
// We update the stop using the liveUpdate method
const updateStopResponse = await api.post(`/${stopId}:liveUpdate`, stopData)
// The response will return an object similar to the following:
// {
// "pending": true,
// "stop": {
// "id": "plans/FQ95Ex714KYeojkeIm77/stops/vgsTiQi85ueWRs1JnXx7",
// "address": { ... }
// ...
// },
// ...
// }
const updateStopResponseJson = updateStopResponse.data
console.log(updateStopResponseJson)
// Now, we have to check if the update was applied to the plan or if it's
// pending a new optimization and distribution. On `pending = true`, we must
// use the re-optimize and re-distribute endpoints to apply the changes to the plan.
if (updateStopResponseJson.pending) {
// We can get the planId from the endpoint response
const planId = updateStopResponseJson.stop.plan
// Let us re-optimize this plan:
const reoptimizationResponse = await api.post(`/${planId}:reoptimize`, {})
// The response will return an operation object similar to the following:
// {
// "id": "operations/FQ95Ex714KYeojkeIm77",
// "done": false,
// "type": "plan_optimization",
// ...
// }
//
// Why an operation? Because the re-optimization can take a long while to
// finish, so we process it in an asynchronous manner.
let operation = reoptimizationResponse.data
console.log(operation)
// Now we need to wait until the re-optimization is done. As this can take a very
// long while depending on the number of stops, we will check it in a loop
// every few seconds.
//
// If the route is small enough, the re-optimization response itself can already
// return a done operation, the condition on this loop prevents us from
// even start polling if that is the case
//
// IMPORTANT: remember to add a delay between each check so
// as to not be rate-limited.
while (!operation.done) {
// Sleep/wait for a certain duration before the next check
await new Promise((resolve) => setTimeout(resolve, 5000)) // 5 seconds delay
const operationProgress = await api.get(`/${operation.id}`)
operation = operationProgress.data
}
// Now the operation is done, which means it finished processing, and it might
// have had a successful result or an error one, let's check for it (the
// result field is not included in the response until the operation finishes):
const result = operation.result
// check for errors: if there is a code and message it means that the
// operation errored with said code and error messages, check the docs for the
// meaning of these
if (result.code && result.message) {
throw new Error(`Operation failed with ${result.code}: ${result.message}`)
}
// otherwise the operation was succesful
console.log(
`Operation successfully optimized ${result.numOptimizedStops} stops`,
)
// but it still might have ignored some stops due to time constraints or other
// problems such as the stop is not reachable:
if (result.skippedStops.length > 0) {
console.log(
`Operation skipped a total of ${result.skippedStops.length} stops: check them manually`,
)
}
// Now finally we can re-distribute this plan and thus apply the changes to the stop that was
// performed before
const redistributeResponse = await api.post(`/${planId}:redistribute`, {})
// the above will return the plan with the `distributed` attribute set to `true`
}
}
# We are using the requests library here to make requests from Python
import requests
from requests.auth import HTTPBasicAuth
import time
# Set the API key
api_key = '<your-api-key>'
auth = HTTPBasicAuth(api_key, '')
def update_stop(stop_id):
# example stop data to update
stop_data = {
'notes': 'Update notes',
'timing': {
'earliestAttemptTime': {
'hour': 12,
'minute': 0,
},
},
}
# We update the stop using the liveUpdate method
update_stop_response = requests.post(
f'https://api.getcircuit.com/public/v0.2b/{stop_id}:liveUpdate',
json=stop_data,
auth=auth
)
# The response will return an object similar to the following:
# {
# "pending": true,
# "stop": {
# "id": "plans/FQ95Ex714KYeojkeIm77/stops/vgsTiQi85ueWRs1JnXx7",
# "address": { ... }
# ...
# },
# ...
# }
update_stop_response_json = update_stop_response.json()
print(update_stop_response_json)
# Now, we have to check if the update was applied to the plan or if it's
# pending a new optimization and distribution. On `pending = true`, we must
# use the re-optimize and re-distribute endpoints to apply the changes to the plan.
if (update_stop_response_json['pending']):
# We can get the planId from the endpoint response
plan_id = update_stop_response_json['stop']['plan']
# Let us re-optimize this plan:
reoptimization_response = requests.post(
f'https://api.getcircuit.com/public/v0.2b/{plan_id}:reoptimize',
auth=auth,
json={}
)
# The response will return an operation object similar to the following:
# {
# "id": "operations/FQ95Ex714KYeojkeIm77",
# "done": false,
# "type": "plan_optimization",
# ...
# }
#
# Why an operation? Because the re-optimization can take a long while to
# finish, so we process it in an asynchronous manner.
operation = reoptimization_response.json()
print(operation)
# Now we need to wait until the re-optimization is done. As this can take a
# very long while depending on the number of stops, we will check it in a loop
# every few seconds.
#
# If the route is small enough, the re-optimization response itself can already
# return a done operation, the condition on this loop prevents us from even
# start polling if that is the case.
#
# IMPORTANT: remember to add a delay between each check so as to not be
# rate-limited.
while not operation['done']:
# Sleep/wait for a certain duration before the next check
time.sleep(5) # 5 seconds delay
operation_check_response = requests.get(
f'https://api.getcircuit.com/public/v0.2b/{operation["id"]}',
auth=auth
)
operation = operation_check_response.json()
# Now the operation is done, which means it finished processing, and it
# might have had a successful result or an error one, let's check for it:
result = operation.get('result')
# Check for errors: if there is a code and message it means that the
# operation errored with said code and error messages, check the docs for
# the meaning of these.
if 'code' in result and 'message' in result:
raise Exception(f"Operation failed with {result['code']}: {result['message']}")
# Otherwise, the operation was successful.
print(f"Operation successfully optimized {result['numOptimizedStops']} stops")
# But it still might have ignored some stops due to time constraints or
# other problems such as the stop is not reachable:
if len(result.get('skippedStops', [])) > 0:
print(f"Operation skipped a total of {len(result['skippedStops'])} stops: check them manually")
# Now finally we can re-distribute this plan and thus apply the changes to the stop that was
# performed before
redistribution_response = requests.post(
f'https://api.getcircuit.com/public/v0.2b/{plan_id}:redistribute',
auth=auth
)
# The above will return the plan with the `distributed` attribute set to `true`.
# We update the stop using the liveUpdate method
curl https://api.getcircuit.com/public/v0.2b/plans/KBoYtDxO3FMh7zJkWdU3/stops/vgsTiQi85ueWRs1JnXx7:liveUpdate \
-X POST \
-H 'Content-Type: application/json' \
-d '{"notes": "Update notes", "timing": {"earliestAttemptTime": {"hour": 12, "minute": 0}}}' \
-u <your-api-key>:
# With stop updated, we start the re-optimization (we
# are considering a plan with the id plans/KBoYtDxO3FMh7zJkWdU3 here):
curl https://api.getcircuit.com/public/v0.2b/plans/KBoYtDxO3FMh7zJkWdU3:reoptimize \
-X POST \
-H 'Content-Type: application/json' \
-d '{}' \
-u <your-api-key>:
# The response will return an operation object similar to the following:
# {
# "id": "operations/FQ95Ex714KYeojkeIm77",
# "done": false,
# "type": "plan_optimization",
# ...
# }
#
# Why an operation? Because the optimization can take a long while to finish, so
# we process it in an asynchronous manner.
# Now, with the returned ID, we can periodically call the GET operation endpoint
# to check it until the `done` field is `true`
curl https://api.getcircuit.com/public/v0.2b/operations/FQ95Ex714KYeojkeIm77 \
-u <your-api-key>:
# Now wait until the operation is done, which means it finished processing, and
# it might have had a successful result or an error one.
#
# The error one will have the `result.code` and `result.message` fields set,
# while the successful one will have the result fields, which include the
# skipped stops and the amount of optimized stops.
# Now finally we can re-distribute this plan and thus apply the changes to the stop that was
# performed before
curl https://api.getcircuit.com/public/v0.2b/plans/KBoYtDxO3FMh7zJkWdU3:redistribute \
-X POST \
-u <your-api-key>: