routific 1.1.1 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80f461bbd6a8c76891619ef0e6eac1a430eb5aa7
4
- data.tar.gz: 659b39f98fcd1b8d45d93cbde3b99f59eb8e0a2a
3
+ metadata.gz: 797096fa2ec79542c96e1e04ecc4063877543b0a
4
+ data.tar.gz: 59a5dee35ac98053066d501915d56f5f73d5c00d
5
5
  SHA512:
6
- metadata.gz: b10a6414354f038e953e4ca3366b8e6322777c92a16c7656b2f02873f49efa80a8499fffe52bc686154bfb39da91cf1b011326845560553db09910c2c7d9d038
7
- data.tar.gz: 50130e568b9c6fd4c5a3bab8212968698b938ca699953d7aaaedbeb84578434fb6bf21606f56a93cbf36ea66cc398342a83a01534e693826bc4f7ce1ee863362
6
+ metadata.gz: 93d962ad00b5d846a28e7a8af20fb64ce314d5476318399fd06c023f155265fc675bfbeacc47b82a85f96e5460229f87c2ad4486714e9b75f437e55cafad331a
7
+ data.tar.gz: 30b2e995c76dfd06fe2dd2f84c7ddf56e88d81b65d8e981c73d0fda7408726cc6a9edce418295da00cf1860762a5fcc264fbede97570203cd71d6dae082fd5fc
data/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  Routific Ruby Gem
2
2
  =================
3
3
 
4
- [![Build Status](https://secure.travis-ci.org/asoesilo/routific-gem.png)](http://travis-ci.org/asoesilo/routific-gem)
4
+ [![Build Status](https://travis-ci.org/routific/routific-gem.svg?branch=master)](https://travis-ci.org/routific/routific-gem)
5
5
 
6
- This Ruby Gem assists users to easily access the [Routific API][1], which is a practical and scalable solution to the Vehicle Routing Problem.
6
+ This Ruby Gem assists users to easily access the [Routific API][1], which is a practical and scalable solution to the Vehicle Routing Problem and Traveling Salesman Problem.
7
7
 
8
8
  [1]: https://routific.com/developers
9
9
 
10
+ Please refer to the full [documentation](https://docs.routific.com) for a detailled documentation of the API.
11
+
10
12
  Installing
11
13
  ----------
12
14
 
@@ -14,100 +16,14 @@ Installing
14
16
 
15
17
  Usage
16
18
  -----
17
- Remember to require it and instantiate it with your token before using it
18
-
19
- ```ruby
20
- require 'routific'
21
- routific = Routific.new(--API_KEY--)
22
- ```
23
-
24
- ### Instance methods
25
-
26
- `routific.setVisit( id, [params] )`
27
-
28
- Sets a visit for the specified location using the specified parameters
29
-
30
- Required arguments in params:
31
-
32
- - location: Object representing the location of the visit.
33
- + lat: Latitude of this location
34
- + lng: Longitude of this location
35
- + name: (optional) Name of the location
36
-
37
- Optional arguments in params:
38
-
39
- - start: the earliest time for this visit. Default value is 00:00, if not specified.
40
- - end: the latest time for this visit. Default value is 23:59, if not specified.
41
- - duration: the length of this visit in minutes
42
- - demand: the capacity that this visit requires
43
-
44
- `routific.setVehicle( id, params )`
45
-
46
- Sets a vehicle with the specified ID and parameters
47
-
48
- Required arguments in params:
49
-
50
- - start_location: Object representing the start location for this vehicle.
51
- + lat: Latitude of this location
52
- + lng: Longitude of this location
53
- + name: (optional) Name of the location
54
-
55
- Optional arguments in params:
56
-
57
- - end_location: Object representing the end location for this vehicle.
58
- + lat: Latitude of this location
59
- + lng: Longitude of this location
60
- + name: (optional) Name of the location
61
-
62
- - shift_start: this vehicle's start shift time (e.g. '08:00'). Default value is 00:00, if not specified.
63
- - shift_end: this vehicle's end shift time (e.g. '17:00'). Default value is 23:59, if not specified.
64
- - capacity: the capacity that this vehicle can load
65
-
66
- `routific.setOptions( params )`
67
-
68
- Sets optional options onto the route requests.
69
-
70
- Optional arguments must be one of the following:
71
-
72
- - traffic
73
- - min_visits_per_vehicle
74
- - balance
75
- - min_vehicles
76
- - shortest_distance
77
-
78
- `routific.getRoute()`
79
-
80
- Returns the route using the previously provided network, visits and fleet information
81
-
82
-
83
- ### Class methods
84
-
85
- `Routific.setToken( token )`
86
-
87
- Sets the default access token to use
88
-
89
- `Routific.getRoute( id, [params] )`
90
-
91
- Returns the route using the specified access token, network, visits and fleet information
92
-
93
-
94
- Both getRoute functions return the Route object, which has the following attributes:
95
-
96
- - status: A sanity check, will always be success when the HTTP code is 200
97
- - fitness: Total travel-time, representing the fitness score of the solution (less is better)
98
- - unserved: List of visits that could not be scheduled.
99
- - vehicleRoutes: The optimized schedule
100
-
101
- Examples
102
- --------
103
- Example 1:
19
+ Remember to require it and set your token before using it
104
20
 
105
21
  ```ruby
106
22
  require 'routific'
23
+ Routific.set_token("YOUR_API_KEY_HERE")
24
+ routific = Routific.new
107
25
 
108
- routific = Routific.new(--API_KEY--)
109
-
110
- routific.setVisit("order_1", {
26
+ routific.set_visit("order_1", {
111
27
  "start" => "9:00",
112
28
  "end" => "12:00",
113
29
  "duration" => 10,
@@ -118,7 +34,18 @@ routific.setVisit("order_1", {
118
34
  }
119
35
  })
120
36
 
121
- routific.setVehicle("vehicle_1", {
37
+ routific.set_visit("order_2", {
38
+ "start" => "9:00",
39
+ "end" => "12:00",
40
+ "duration" => 10,
41
+ "location" => {
42
+ "name"=> "3780 Arbutus",
43
+ "lat"=> 49.2474624,
44
+ "lng"=> -123.1532338
45
+ }
46
+ })
47
+
48
+ routific.set_vehicle("vehicle_1", {
122
49
  "start_location" => {
123
50
  "name" => "800 Kingsway",
124
51
  "lat" => 49.2553636,
@@ -133,50 +60,125 @@ routific.setVehicle("vehicle_1", {
133
60
  "shift_end" => "12:00",
134
61
  })
135
62
 
136
- route = routific.getRoute()
63
+ routific.set_options({
64
+ "polylines" => true
65
+ })
66
+
67
+ route = routific.get_route()
68
+
69
+
70
+ puts route.status # => "success"
71
+ puts route.total_travel_time # => 29
72
+ puts route.polylines["vehicle_1"] # => s`i}|AbxswiFnn@gdBvG?vVf@jC?nZRb[f@j\z@nZRnZRj\f@nZz
73
+
74
+ vehicle_routes = route.vehicle_routes
75
+
76
+ v1_route = vehicle_routes["vehicle_1"]
77
+ puts v1_route.length # => 4 (start -> order_1 -> order_2 -> end)
78
+
79
+ v1_route.each do |w|
80
+ puts "#{w.location_id}: #{w.arrival_time} ~ #{w.finish_time}"
81
+ end
82
+ # vehicle_1_start: 08:49 ~
83
+ # order_1: 09:00 ~ 09:10
84
+ # order_2: 09:18 ~ 09:28
85
+ # vehicle_1_end: 09:38 ~
86
+
137
87
  ```
138
88
 
139
- Example 2:
89
+ ### Class methods
90
+
91
+ `Routific.set_token( token )` sets the authentication token to use.
140
92
 
141
- ```ruby
142
- require 'routific'
93
+ ### Instance methods
143
94
 
144
- Routific.setToken(--API_KEY--)
145
-
146
- visits = {
147
- "order_1" => {
148
- "start" => "9:00",
149
- "end" => "12:00",
150
- "duration" => 10,
151
- "location" => {
152
- "name" => "6800 Cambie",
153
- "lat" => 49.227107,
154
- "lng" => -123.1163085
155
- }
156
- }
157
- }
158
-
159
- fleet = {
160
- "vehicle_1" => {
161
- "start_location" => {
162
- "name" => "800 Kingsway",
163
- "lat" => 49.2553636,
164
- "lng" => -123.0873365
165
- },
166
- "end_location" => {
167
- "name" => "800 Kingsway",
168
- "lat" => 49.2553636,
169
- "lng" => -123.0873365
170
- },
171
- "shift_start" => "8:00",
172
- "shift_end" => "12:00"
173
- }
174
- }
95
+ #### `routific.set_visit( id, params )`
175
96
 
176
- data = {
177
- visits: visits,
178
- fleet: fleet
179
- }
97
+ Sets a visit with the specified id and parameters:
180
98
 
181
- route = Routific.getRoute(data)
99
+ - `location` (*required*): Object representing the location of the visit.
100
+ + lat: Latitude of this location
101
+ + lng: Longitude of this location
102
+ + name: (optional) Name of the location
103
+ - `start`: the earliest time for this visit. Default value is 00:00, if not specified.
104
+ - `end`: the latest time for this visit. Default value is 23:59, if not specified.
105
+ - `duration`: the length of this visit in minutes
106
+ - `demand`: the capacity that this visit requires
107
+ - `priority`: higher priority visits are more likely to be served
108
+ - `type`: restrict the vehicle that can serve this visit
109
+ - `time_windows`: specify different time-windows for serving the visit.
110
+ It should be an array of hashes: `[ { "start" => "08:00", "end" => "12:00" } ]`
111
+
112
+ #### `routific.set_vehicle( id, params )`
113
+
114
+ Sets a vehicle with the specified ID and parameters:
115
+ - `start_location` (*required*): Object representing the start location for this vehicle.
116
+ + lat: Latitude of this location
117
+ + lng: Longitude of this location
118
+ + name: (optional) Name of the location
119
+ - `end_location`: Object representing the end location for this vehicle.
120
+ + lat: Latitude of this location
121
+ + lng: Longitude of this location
122
+ + name: (optional) Name of the location
123
+ - `shift_start`: this vehicle's start shift time (e.g. '08:00'). Default value is 00:00, if not specified.
124
+ - `shift_end`: this vehicle's end shift time (e.g. '17:00'). Default value is 23:59, if not specified.
125
+ - `capacity`: the capacity that this vehicle can load
126
+ - `type`: restrict the visit this vehicle can serve
127
+ - `speed`: vehicle speed
128
+ - `min_visits`: minimum number of visits that should be assigned to this vehicle
129
+ - `strict_start`: force the departure time to be `shift_start`
130
+ - `breaks`: specify breaks for the driver.
131
+ It should be an array of hashes: `[ { "id" => "lunch", "start" => "12:00", "end" => "12:30" } ]`
132
+
133
+ #### `routific.set_options( params )`
134
+
135
+ Sets optional options onto the route requests.
136
+ Optional arguments must be one of the following:
137
+
138
+ - `traffic`
139
+ - `min_visits_per_vehicle`
140
+ - `balance`
141
+ - `min_vehicles`
142
+ - `shortest_distance`
143
+ - `squash_duration`
144
+ - `max_vehicle_overtime`
145
+ - `max_visit_lateness`
146
+
147
+ #### `routific.get_route()`
148
+
149
+ Returns an optimized route using the previously provided visits, fleet and options.
150
+ The request may timeout if the problem is too large.
151
+
152
+ It returns a route object with the following attributes:
153
+ - `status`: A sanity check
154
+ - `unserved`: List of visits that could not be scheduled.
155
+ - `vehicle_routes`: The optimized schedule
156
+ - other attributes that you can find in the [full documentation](https://docs.routific.com)
157
+
158
+ The `vehicle_routes` attribute is a hash mapping vehicle ID to the corresponding route, represented as an array of waypoints: `{ "vehicle_1" => [ way_point_1, way_point_2, way_point_3, way_point_4 ] }`
159
+
160
+ The waypoint object has the following attributes:
161
+ - `location_id`
162
+ - `arrival_time`
163
+ - `finish_time`
164
+ - other attributes that you can find in the [full documentation](https://docs.routific.com)
165
+
166
+ #### `routific.get_route_async()`
167
+
168
+ For requests with 60 visits and more, it is recommended to make asynchronous call to the API.
169
+
170
+ ```ruby
171
+ job = routific.get_route_async()
172
+ puts job.status # => "pending"
173
+ sleep 5
174
+ job.fetch
175
+ puts job.status # => "finished"
176
+ route = job.route
182
177
  ```
178
+
179
+ It returns a job object with the following attibutes:
180
+ - `status`: status of the task ('pending', 'processing', 'finished', 'error')
181
+ - `id`: a unique identifier for the task
182
+ - `created_at`, `finished_at`: creation and finish times
183
+ - `input`: the data used for the request
184
+ - `route`: a route object
@@ -1,21 +1,22 @@
1
- require 'rest-client'
2
- require 'json'
3
-
4
1
  require_relative './routific/location'
5
2
  require_relative './routific/visit'
3
+ require_relative './routific/break'
6
4
  require_relative './routific/vehicle'
7
5
  require_relative './routific/route'
8
6
  require_relative './routific/way_point'
9
7
  require_relative './routific/options'
8
+ require_relative './routific/job'
9
+
10
+ require_relative './util'
10
11
 
11
12
  # Main class of this gem
12
13
  class Routific
13
- attr_reader :token, :visits, :fleet, :options
14
+ attr_reader :visits, :fleet, :options
15
+ @@token = nil
14
16
 
15
17
  # Constructor
16
- # token: Access token for Routific API
17
- def initialize(token)
18
- @token = token
18
+ def initialize()
19
+ Routific.validate_token
19
20
  @visits = {}
20
21
  @fleet = {}
21
22
  @options = {}
@@ -24,75 +25,61 @@ class Routific
24
25
  # Sets a visit for the specified location using the specified parameters
25
26
  # id: ID of location to visit
26
27
  # params: parameters for this visit
27
- def setVisit(id, params={})
28
+ def set_visit(id, params={})
28
29
  visits[id] = RoutificApi::Visit.new(id, params)
29
30
  end
30
31
 
31
32
  # Sets a vehicle with the specified ID and parameters
32
33
  # id: vehicle ID
33
34
  # params: parameters for this vehicle
34
- def setVehicle(id, params)
35
+ def set_vehicle(id, params)
35
36
  fleet[id] = RoutificApi::Vehicle.new(id, params)
36
37
  end
37
38
 
38
39
  # Sets options with the specified params
39
40
  # params: parameters for these options
40
- def setOptions(params)
41
+ def set_options(params)
41
42
  @options = RoutificApi::Options.new(params)
42
43
  end
43
44
 
44
45
  # Returns the route using the previously provided visits and fleet information
45
- def getRoute
46
+ def get_route
46
47
  data = {
47
48
  visits: visits,
48
49
  fleet: fleet
49
50
  }
50
51
 
51
52
  data[:options] = options if options
52
- Routific.getRoute(data, token)
53
+ result = Util.send_request("POST", "vrp", Routific.token, data)
54
+ RoutificApi::Route.parse(result)
55
+ end
56
+
57
+ def get_route_async
58
+ data = {
59
+ visits: visits,
60
+ fleet: fleet
61
+ }
62
+
63
+ data[:options] = options if options
64
+ result = Util.send_request("POST", "vrp-long", Routific.token, data)
65
+ RoutificApi::Job.new(result["job_id"], data)
53
66
  end
54
67
 
55
68
  class << self
56
69
  # Sets the default access token to use
57
- def setToken(token)
70
+ def set_token(token)
58
71
  @@token = token
72
+ validate_token
73
+ @@token = Util.prefix_token(@@token)
59
74
  end
60
75
 
61
76
  def token
62
77
  @@token
63
78
  end
64
79
 
65
- # Returns the route using the specified access token, visits and fleet information
66
- # If no access token is provided, the default access token previously set is used
67
- # If the default access token either is nil or has not been set, an ArgumentError is raised
68
- def getRoute(data, token = @@token)
69
- if token.nil?
70
- raise ArgumentError, "access token must be set"
71
- end
72
-
73
- # Prefix the token with "bearer " if missing during assignment
74
- prefixed_token = (/bearer /.match(token).nil?) ? "bearer #{token}" : token
75
-
76
- begin
77
- # Sends HTTP request to Routific API server
78
- response = RestClient.post('https://api.routific.com/v1/vrp',
79
- data.to_json,
80
- 'Authorization' => prefixed_token,
81
- content_type: :json,
82
- accept: :json
83
- )
84
-
85
- # Parse the HTTP request response to JSON
86
- jsonResponse = JSON.parse(response)
87
-
88
- # Parse the JSON representation into a RoutificApi::Route object
89
- RoutificApi::Route.parse(jsonResponse)
90
- rescue => e
91
- puts e
92
- errorResponse = JSON.parse e.response.body
93
- puts "Received HTTP #{e.message}: #{errorResponse["error"]}"
94
- nil
95
- end
80
+ def validate_token
81
+ raise ArgumentError, "access token must be set" if @@token.nil?
96
82
  end
83
+
97
84
  end
98
85
  end
@@ -0,0 +1,59 @@
1
+ module RoutificApi
2
+ # This class represent a vehicle break
3
+ class Break
4
+ REQUIRED_PARAMS = %w{ id start end }
5
+
6
+ attr_reader *REQUIRED_PARAMS
7
+ attr_reader :in_transit
8
+
9
+ # Constructor
10
+ #
11
+ # Required arguments in params:
12
+ # id: unique identifier for the break (e.g. 'lunch-break')
13
+ # start: start time of the break (e.g. '12:00')
14
+ # end: end time of the break (e.g. '12:30')
15
+ #
16
+ # Optional argument in params:
17
+ # in_transit: whether the vehicle can be in movement during the break (e.g. true). The default value is false.
18
+ def initialize(params)
19
+ validate(params)
20
+
21
+ @id = params["id"]
22
+ @start = params["start"]
23
+ @end = params["end"]
24
+ @in_transit = params["in_transit"] unless params["in_transit"].nil?
25
+ end
26
+
27
+ def ==(another_break)
28
+ self.id == another_break.id &&
29
+ self.start == another_break.start &&
30
+ self.end == another_break.end &&
31
+ self.in_transit == another_break.in_transit
32
+ end
33
+
34
+ def to_json(options = nil)
35
+ as_json(options).to_json
36
+ end
37
+
38
+ def as_json(options = nil)
39
+ jsonData = {}
40
+ jsonData["id"] = self.id
41
+ jsonData["start"] = self.start
42
+ jsonData["end"] = self.end
43
+ jsonData["in_transit"] = self.in_transit unless self.in_transit.nil?
44
+
45
+ jsonData
46
+ end
47
+
48
+ private
49
+ # Validates the parameters being provided
50
+ # Raises an ArgumentError if any of the required parameters is not provided.
51
+ def validate(params)
52
+ REQUIRED_PARAMS.each do |param|
53
+ if params[param].nil?
54
+ raise ArgumentError, "'#{param}' parameter must be provided in break"
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ module RoutificApi
2
+ # This class represents a job returned by vrp-long
3
+ class Job
4
+ FIELDS = [:status, :created_at, :finished_at]
5
+ attr_reader *FIELDS
6
+ attr_reader :input, :id, :route
7
+
8
+ # Constructor
9
+ def initialize(id, input)
10
+ @id = id
11
+ @input = input
12
+ @status = 'pending'
13
+ end
14
+
15
+ def fetch
16
+ job_data = Util.send_request("GET", "jobs/#{@id}")
17
+
18
+ FIELDS.each do |field|
19
+ instance_variable_set "@#{field}", job_data[field.to_s]
20
+ end
21
+ @route = RoutificApi::Route.parse(job_data["output"]) if job_data["output"]
22
+ job_data['status']
23
+ end
24
+ end
25
+ end
@@ -1,19 +1,17 @@
1
1
  module RoutificApi
2
2
  # This class represents a set of options for the request
3
3
  class Options
4
- VALID_PARAMS = %w{ traffic min_visits_per_vehicle balance min_vehicles shortest_distance }
4
+ VALID_PARAMS = %w{ traffic min_visits_per_vehicle balance min_vehicles shortest_distance squash_durations max_vehicle_overtime max_visit_lateness polylines }
5
5
 
6
6
  attr_reader *VALID_PARAMS
7
7
 
8
8
  def initialize(params)
9
9
  # Validates the provided parameters
10
10
  validate(params)
11
-
12
- @traffic = params["traffic"]
13
- @min_visits_per_vehicle = params["min_visits_per_vehicle"]
14
- @balance = params["balance"]
15
- @min_vehicles = params["min_vehicles"]
16
- @shortest_distance = params["shortest_distance"]
11
+
12
+ VALID_PARAMS.each do |param|
13
+ instance_variable_set "@#{param}", params[param]
14
+ end
17
15
  end
18
16
 
19
17
  def to_json(options)
@@ -24,19 +22,17 @@ module RoutificApi
24
22
  # def to_json(options = nil)
25
23
  def as_json(options = nil)
26
24
  jsonData = {}
27
- jsonData["traffic"] = self.traffic if self.traffic
28
- jsonData["min_visits_per_vehicle"] = self.min_visits_per_vehicle if self.min_visits_per_vehicle
29
- jsonData["balance"] = self.balance if self.balance
30
- jsonData["shortest_distance"] = self.shortest_distance if self.shortest_distance
31
- jsonData["min_vehicles"] = self.min_vehicles if self.min_vehicles
32
-
25
+
26
+ VALID_PARAMS.each do |param|
27
+ instance_var = instance_variable_get "@#{param}"
28
+ jsonData[param] = instance_var if instance_var
29
+ end
33
30
  jsonData
34
31
  end
35
32
 
36
33
  private
37
34
  # Validates the parameters being provided
38
35
  # Raises an ArgumentError if any of the provided params is not supported
39
- # Supported params are traffic, min_visits_per_vehicle, and balance
40
36
  def validate(params)
41
37
  invalid = params.keys.reject { |k| VALID_PARAMS.include?(k) }
42
38
 
@@ -1,46 +1,63 @@
1
1
  module RoutificApi
2
2
  # This class represents the resulting route returned by the Routific API
3
3
  class Route
4
- attr_reader :status, :fitness, :unserved, :vehicleRoutes
4
+ FIELDS = [
5
+ :status,
6
+ :unserved, :num_unserved,
7
+ :distances, :total_distance,
8
+ :total_working_time, :total_travel_time,
9
+ :total_break_time, :total_idle_time,
10
+ :total_visit_lateness, :num_late_visits,
11
+ :vehicle_overtime, :total_vehicle_overtime,
12
+ :polylines,
13
+ ]
14
+
15
+ attr_reader *FIELDS
16
+ attr_reader :vehicle_routes
5
17
 
6
18
  # Constructor
7
- def initialize(status, fitness, unserved)
8
- @status = status
9
- @fitness = fitness
10
- @unserved = unserved
11
- @vehicleRoutes = Hash.new()
19
+ def initialize(data)
20
+ FIELDS.each do |field|
21
+ instance_variable_set "@#{field}", data[field]
22
+ end
23
+ add_solution(data[:solution] || {})
24
+ end
25
+
26
+ def add_solution(solution)
27
+ @vehicle_routes = {}
28
+
29
+ solution.each do |vehicle_name, way_points|
30
+ # Get all way points for this vehicle
31
+ way_points.each do |waypoint_info|
32
+ # Get all information for this way point
33
+ way_point = RoutificApi::WayPoint.new(waypoint_info)
34
+ add_way_point(vehicle_name, way_point)
35
+ end
36
+ end
12
37
  end
13
38
 
14
39
  # Adds a new way point for the specified vehicle
15
- def addWayPoint(vehicle_name, way_point)
16
- if @vehicleRoutes[vehicle_name].nil?
40
+ def add_way_point(vehicle_name, way_point)
41
+ if @vehicle_routes[vehicle_name].nil?
17
42
  # No previous way point was added for the specified vehicle, so create a new array
18
- @vehicleRoutes[vehicle_name] = []
43
+ @vehicle_routes[vehicle_name] = []
19
44
  end
20
45
  # Adds the provided way point for the specified vehicle
21
- @vehicleRoutes[vehicle_name] << way_point
46
+ @vehicle_routes[vehicle_name] << way_point
22
47
  end
23
48
 
24
49
  class << self
25
50
  # Parse the JSON representation of a route, and return it as a Route object
26
- def parse(routeJson)
27
- status = routeJson["status"]
28
- fitness = routeJson["fitness"]
29
- unserved = routeJson["unserved"]
30
- route = RoutificApi::Route.new(status, fitness, unserved)
51
+ def parse(data)
52
+ data = process_data(data)
53
+ RoutificApi::Route.new(data)
54
+ end
31
55
 
32
- # Get way points for each vehicles
33
- routeJson["solution"].each do |vehicle_name, way_points|
34
- # Get all way points for this vehicle
35
- way_points.each do |waypoint_info|
36
- # Get all information for this way point
37
- way_point = RoutificApi::WayPoint.new(waypoint_info)
38
- route.addWayPoint(vehicle_name, way_point)
39
- end
56
+ def process_data(hash)
57
+ hash.keys.each do |key|
58
+ hash[(key.to_sym rescue key) || key] = hash.delete(key)
40
59
  end
41
-
42
- # Return the resulting Route object
43
- route
60
+ hash
44
61
  end
45
62
  end
46
63
  end
@@ -1,7 +1,10 @@
1
+ require_relative './break'
2
+
1
3
  module RoutificApi
2
4
  # This class represents a vehicle in the fleet
3
5
  class Vehicle
4
- attr_accessor :id, :start_location, :end_location, :shift_start, :shift_end, :capacity
6
+ attr_accessor :id, :start_location, :end_location, :shift_start,
7
+ :shift_end, :capacity, :strict_start, :min_visits, :speed, :breaks, :type
5
8
 
6
9
  # Constructor
7
10
  #
@@ -24,6 +27,13 @@ module RoutificApi
24
27
  @shift_start = params["shift_start"]
25
28
  @shift_end = params["shift_end"]
26
29
  @capacity = params["capacity"]
30
+ @strict_start = params["strict_start"]
31
+ @min_visits = params["min_visits"]
32
+ @speed = params["speed"]
33
+ @type = params["type"]
34
+ if params["breaks"]
35
+ @breaks = params["breaks"].map{ |brk| RoutificApi::Break.new(brk) }
36
+ end
27
37
  end
28
38
 
29
39
  def to_json(options=nil)
@@ -33,14 +43,21 @@ module RoutificApi
33
43
  # Returns the JSON representation of this object
34
44
  # def to_json(options = nil)
35
45
  def as_json(options = nil)
36
- jsonData = {}
37
- jsonData["start_location"] = self.start_location.as_json
38
- jsonData["end_location"] = self.end_location.as_json if self.end_location
39
- jsonData["shift_start"] = self.shift_start if self.shift_start
40
- jsonData["shift_end"] = self.shift_end if self.shift_end
41
- jsonData["capacity"] = self.capacity if self.capacity
42
-
43
- jsonData
46
+ json_data = {}
47
+ json_data["start_location"] = self.start_location.as_json
48
+ json_data["end_location"] = self.end_location.as_json if self.end_location
49
+ json_data["shift_start"] = self.shift_start if self.shift_start
50
+ json_data["shift_end"] = self.shift_end if self.shift_end
51
+ json_data["capacity"] = self.capacity if self.capacity
52
+ json_data["strict_start"] = self.strict_start if not self.strict_start.nil?
53
+ json_data["min_visits"] = self.min_visits if self.min_visits
54
+ json_data["speed"] = self.speed if self.speed
55
+ json_data["type"] = self.type if self.type
56
+ if self.breaks
57
+ json_data["breaks"] = self.breaks.map{ |brk| brk.as_json }
58
+ end
59
+
60
+ json_data
44
61
  end
45
62
 
46
63
 
@@ -1,7 +1,8 @@
1
1
  module RoutificApi
2
2
  # This class represents a location to be visited
3
3
  class Visit
4
- attr_reader :id, :start, :end, :duration, :demand, :location
4
+ attr_reader :id, :start, :end, :duration, :demand, :location, :priority,
5
+ :time_windows, :type
5
6
 
6
7
  # Constructor
7
8
  #
@@ -19,6 +20,11 @@ module RoutificApi
19
20
  @duration = params["duration"]
20
21
  @demand = params["demand"]
21
22
  @location = RoutificApi::Location.new(params["location"])
23
+ @priority = params["priority"]
24
+ @type = params["type"]
25
+ if params["time_windows"]
26
+ @time_windows = params["time_windows"].map{ |tw| TimeWindow.new(tw) }
27
+ end
22
28
  end
23
29
 
24
30
  def to_json(options)
@@ -28,14 +34,19 @@ module RoutificApi
28
34
  # Returns the JSON representation of this object
29
35
  # def to_json(options = nil)
30
36
  def as_json(options = nil)
31
- jsonData = {}
32
- jsonData["start"] = self.start if self.start
33
- jsonData["end"] = self.end if self.end
34
- jsonData["duration"] = self.duration if self.duration
35
- jsonData["demand"] = self.demand if self.demand
36
- jsonData["location"] = self.location.as_json
37
-
38
- jsonData
37
+ json_data = {}
38
+ json_data["start"] = self.start if self.start
39
+ json_data["end"] = self.end if self.end
40
+ json_data["duration"] = self.duration if self.duration
41
+ json_data["demand"] = self.demand if self.demand
42
+ json_data["location"] = self.location.as_json
43
+ json_data["priority"] = self.priority if self.priority
44
+ json_data["type"] = self.type if self.type
45
+ if self.time_windows
46
+ json_data["time_windows"] = self.time_windows.map{ |tw| tw.as_json }
47
+ end
48
+
49
+ json_data
39
50
  end
40
51
 
41
52
  private
@@ -47,5 +58,33 @@ module RoutificApi
47
58
  raise ArgumentError, "'location' parameter must be provided"
48
59
  end
49
60
  end
61
+
62
+ public
63
+ class TimeWindow
64
+ attr_reader :start, :end
65
+
66
+ # Constructor
67
+ #
68
+ # Optional arguments
69
+ # start: start of the time-window
70
+ # end: end of the time-window
71
+ def initialize(params)
72
+ @start = params["start"]
73
+ @end = params["end"]
74
+ end
75
+
76
+ def ==(another_tw)
77
+ self.start == another_tw.start &&
78
+ self.end == another_tw.end
79
+ end
80
+
81
+ def as_json
82
+ json_data = {}
83
+ json_data["start"] = self.start if self.start
84
+ json_data["end"] = self.end if self.end
85
+
86
+ json_data
87
+ end
88
+ end
50
89
  end
51
90
  end
@@ -1,14 +1,21 @@
1
1
  module RoutificApi
2
2
  # This class represents a location to visit in the route
3
3
  class WayPoint
4
- attr_reader :location_id, :arrival_time, :finish_time, :location_name
4
+ FIELDS = [
5
+ :location_id, :location_name,
6
+ :arrival_time, :finish_time,
7
+ :idle_time,
8
+ :id, :break, :start, :end, :in_transit, # breaks
9
+ :too_late, :late_by # visit lateness
10
+ ]
11
+
12
+ attr_reader *FIELDS
5
13
 
6
14
  # Constructor
7
15
  def initialize(options = {})
8
- @location_id = options['location_id']
9
- @arrival_time = options['arrival_time']
10
- @finish_time = options['finish_time']
11
- @location_name = options['location_name']
16
+ FIELDS.each do |field|
17
+ instance_variable_set "@#{field}", options[field.to_s]
18
+ end
12
19
  end
13
20
  end
14
21
  end
@@ -0,0 +1,41 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module Util
5
+ BASE_URL = 'https://api.routific.com/v1/'
6
+
7
+ ##
8
+ # method: "GET", "POST"
9
+ # endpoint: "vrp", "vrp-long", "job"
10
+ # token: if nil, raise ArgumentError; if missing "bearer", prefix
11
+ # data: only for POST requests
12
+ #
13
+ def self.send_request(method, endpoint, token = nil, data = nil)
14
+ url = BASE_URL + endpoint
15
+ headers = {
16
+ content_type: :json,
17
+ accept: :json
18
+ }
19
+ headers['Authorization'] = token if token
20
+ begin
21
+ # Sends HTTP request to Routific API server
22
+ response = nil
23
+ if method == 'GET'
24
+ response = RestClient.get(url, headers)
25
+ elsif method == 'POST'
26
+ response = RestClient.post(url, data.to_json, headers)
27
+ end
28
+
29
+ return JSON.parse(response)
30
+ rescue => e
31
+ puts e
32
+ errorResponse = JSON.parse e.response.body
33
+ puts "Received HTTP #{e.message}: #{errorResponse["error"]}"
34
+ nil
35
+ end
36
+ end
37
+
38
+ def self.prefix_token(token)
39
+ (/bearer /.match(token).nil?) ? "bearer #{token}" : token
40
+ end
41
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: routific
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
- - Marc Kuo
8
- - Andre Soesilo
7
+ - Routific
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2016-08-12 00:00:00.000000000 Z
11
+ date: 2019-11-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rest-client
@@ -17,28 +16,28 @@ dependencies:
17
16
  requirements:
18
17
  - - "~>"
19
18
  - !ruby/object:Gem::Version
20
- version: '1.7'
19
+ version: 2.0.1
21
20
  type: :runtime
22
21
  prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - "~>"
26
25
  - !ruby/object:Gem::Version
27
- version: '1.7'
26
+ version: 2.0.1
28
27
  - !ruby/object:Gem::Dependency
29
28
  name: json
30
29
  requirement: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - "~>"
33
32
  - !ruby/object:Gem::Version
34
- version: '1.8'
33
+ version: 2.3.0
35
34
  type: :runtime
36
35
  prerelease: false
37
36
  version_requirements: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - "~>"
40
39
  - !ruby/object:Gem::Version
41
- version: '1.8'
40
+ version: 2.3.0
42
41
  - !ruby/object:Gem::Dependency
43
42
  name: rspec
44
43
  requirement: !ruby/object:Gem::Requirement
@@ -57,16 +56,16 @@ dependencies:
57
56
  name: faker
58
57
  requirement: !ruby/object:Gem::Requirement
59
58
  requirements:
60
- - - "~>"
59
+ - - ">="
61
60
  - !ruby/object:Gem::Version
62
- version: '1.4'
61
+ version: 1.6.2
63
62
  type: :development
64
63
  prerelease: false
65
64
  version_requirements: !ruby/object:Gem::Requirement
66
65
  requirements:
67
- - - "~>"
66
+ - - ">="
68
67
  - !ruby/object:Gem::Version
69
- version: '1.4'
68
+ version: 1.6.2
70
69
  - !ruby/object:Gem::Dependency
71
70
  name: dotenv
72
71
  requirement: !ruby/object:Gem::Requirement
@@ -82,24 +81,27 @@ dependencies:
82
81
  - !ruby/object:Gem::Version
83
82
  version: '0.11'
84
83
  description: Gem to use Routific API
85
- email: asoesilo@live.com
84
+ email: support@routific.com
86
85
  executables: []
87
86
  extensions: []
88
87
  extra_rdoc_files: []
89
88
  files:
90
89
  - README.md
91
90
  - lib/routific.rb
91
+ - lib/routific/break.rb
92
+ - lib/routific/job.rb
92
93
  - lib/routific/location.rb
93
94
  - lib/routific/options.rb
94
95
  - lib/routific/route.rb
95
96
  - lib/routific/vehicle.rb
96
97
  - lib/routific/visit.rb
97
98
  - lib/routific/way_point.rb
99
+ - lib/util.rb
98
100
  homepage: https://routific.com/
99
101
  licenses:
100
102
  - MIT
101
103
  metadata:
102
- source_code: https://github.com/asoesilo/routific-gem
104
+ source_code: https://github.com/routific/routific-gem
103
105
  post_install_message:
104
106
  rdoc_options: []
105
107
  require_paths:
@@ -116,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
118
  version: '0'
117
119
  requirements: []
118
120
  rubyforge_project:
119
- rubygems_version: 2.2.2
121
+ rubygems_version: 2.5.2.3
120
122
  signing_key:
121
123
  specification_version: 4
122
124
  summary: routific API