routific 1.1.1 → 1.7.4

Sign up to get free protection for your applications and to get access to all the features.
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