routific 1.1.2 → 1.7.1

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: a92a096b9aa04f8eed0c72c8b45d4ca262fd6f15
4
- data.tar.gz: fdfe5a0030a05a98a5427484c4368325a7b16d93
3
+ metadata.gz: 6449926da7cc8d2f56c751103acc80b3f4301f58
4
+ data.tar.gz: 3e9d86b6c6a2890ae96b0acc862b0d13dbeea134
5
5
  SHA512:
6
- metadata.gz: d20604a843d68aa9a10bc72ba6e63305b7bb65f0a729c2a58e08dab12c5829ff30eadbe31076d0927c14d351ffe82edf8492ba875bad49c85d6b933afc86ee32
7
- data.tar.gz: be1a38259698b29c679446377ce38f1d09497cf2be9be87f2f960bf5f9fa4a5df04781f7eab15fe7ad73b4f2c28003ee747f4cc0b84d1213453e882e3f8a077e
6
+ metadata.gz: 3cbed53fcca005a88e1f9a11f198d6a76928903340935d0ac0117a2e0f193d2f3b744de82ee6a5ad2ef545da4414d433cc664e1c4e1236ee90eefb42b26423ca
7
+ data.tar.gz: 0c4ef3aa08f1974a97b2a6325331fa6394c414b7edec36845bfcf145af95da514ce68a0cb4dafdcfd76c48c88edfdaf7d7b01474813ffa322427695ccd7c1c0f
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/routific/routific-gem.png)](http://travis-ci.org/routific/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
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.
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,120 @@ routific.setVehicle("vehicle_1", {
133
60
  "shift_end" => "12:00",
134
61
  })
135
62
 
136
- route = routific.getRoute()
63
+ route = routific.get_route()
64
+
65
+
66
+ puts route.status # => "success"
67
+ puts route.total_travel_time # => 29
68
+
69
+ vehicle_routes = route.vehicle_routes
70
+
71
+ v1_route = vehicle_routes["vehicle_1"]
72
+ puts v1_route.length # => 4 (start -> order_1 -> order_2 -> end)
73
+
74
+ v1_route.each do |w|
75
+ puts "#{w.location_id}: #{w.arrival_time} ~ #{w.finish_time}"
76
+ end
77
+ # vehicle_1_start: 08:49 ~
78
+ # order_1: 09:00 ~ 09:10
79
+ # order_2: 09:18 ~ 09:28
80
+ # vehicle_1_end: 09:38 ~
81
+
137
82
  ```
138
83
 
139
- Example 2:
84
+ ### Class methods
140
85
 
141
- ```ruby
142
- require 'routific'
86
+ `Routific.set_token( token )` sets the authentication token to use.
143
87
 
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
- }
88
+ ### Instance methods
89
+
90
+ #### `routific.set_visit( id, params )`
175
91
 
176
- data = {
177
- visits: visits,
178
- fleet: fleet
179
- }
92
+ Sets a visit with the specified id and parameters:
180
93
 
181
- route = Routific.getRoute(data)
94
+ - `location` (*required*): Object representing the location of the visit.
95
+ + lat: Latitude of this location
96
+ + lng: Longitude of this location
97
+ + name: (optional) Name of the location
98
+ - `start`: the earliest time for this visit. Default value is 00:00, if not specified.
99
+ - `end`: the latest time for this visit. Default value is 23:59, if not specified.
100
+ - `duration`: the length of this visit in minutes
101
+ - `demand`: the capacity that this visit requires
102
+ - `priority`: higher priority visits are more likely to be served
103
+ - `type`: restrict the vehicle that can serve this visit
104
+ - `time_windows`: specify different time-windows for serving the visit.
105
+ It should be an array of hashes: `[ { "start" => "08:00", "end" => "12:00" } ]`
106
+
107
+ #### `routific.set_vehicle( id, params )`
108
+
109
+ Sets a vehicle with the specified ID and parameters:
110
+ - `start_location` (*required*): Object representing the start location for this vehicle.
111
+ + lat: Latitude of this location
112
+ + lng: Longitude of this location
113
+ + name: (optional) Name of the location
114
+ - `end_location`: Object representing the end location for this vehicle.
115
+ + lat: Latitude of this location
116
+ + lng: Longitude of this location
117
+ + name: (optional) Name of the location
118
+ - `shift_start`: this vehicle's start shift time (e.g. '08:00'). Default value is 00:00, if not specified.
119
+ - `shift_end`: this vehicle's end shift time (e.g. '17:00'). Default value is 23:59, if not specified.
120
+ - `capacity`: the capacity that this vehicle can load
121
+ - `type`: restrict the visit this vehicle can serve
122
+ - `speed`: vehicle speed
123
+ - `min_visits`: minimum number of visits that should be assigned to this vehicle
124
+ - `strict_start`: force the departure time to be `shift_start`
125
+ - `breaks`: specify breaks for the driver.
126
+ It should be an array of hashes: `[ { "id" => "lunch", "start" => "12:00", "end" => "12:30" } ]`
127
+
128
+ #### `routific.set_options( params )`
129
+
130
+ Sets optional options onto the route requests.
131
+ Optional arguments must be one of the following:
132
+
133
+ - `traffic`
134
+ - `min_visits_per_vehicle`
135
+ - `balance`
136
+ - `min_vehicles`
137
+ - `shortest_distance`
138
+ - `squash_duration`
139
+ - `max_vehicle_overtime`
140
+ - `max_visit_lateness`
141
+
142
+ #### `routific.get_route()`
143
+
144
+ Returns an optimized route using the previously provided visits, fleet and options.
145
+ The request may timeout if the problem is too large.
146
+
147
+ It returns a route object with the following attributes:
148
+ - `status`: A sanity check
149
+ - `unserved`: List of visits that could not be scheduled.
150
+ - `vehicle_routes`: The optimized schedule
151
+ - other attributes that you can find in the [full documentation](https://docs.routific.com)
152
+
153
+ 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 ] }`
154
+
155
+ The waypoint object has the following attributes:
156
+ - `location_id`
157
+ - `arrival_time`
158
+ - `finish_time`
159
+ - other attributes that you can find in the [full documentation](https://docs.routific.com)
160
+
161
+ #### `routific.get_route_async()`
162
+
163
+ For requests with 60 visits and more, it is recommended to make asynchronous call to the API.
164
+
165
+ ```ruby
166
+ job = routific.get_route_async()
167
+ puts job.status # => "pending"
168
+ sleep 5
169
+ job.fetch
170
+ puts job.status # => "finished"
171
+ route = job.route
182
172
  ```
173
+
174
+ It returns a job object with the following attibutes:
175
+ - `status`: status of the task ('pending', 'processing', 'finished', 'error')
176
+ - `id`: a unique identifier for the task
177
+ - `created_at`, `finished_at`: creation and finish times
178
+ - `input`: the data used for the request
179
+ - `route`: a route object
data/lib/routific.rb CHANGED
@@ -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 }
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,23 +1,29 @@
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, :unserved, :vehicleRoutes, :total_travel_time, :total_idle_time
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_overtime
12
+ ]
13
+
14
+ attr_reader *FIELDS
15
+ attr_reader :vehicle_routes
5
16
 
6
17
  # Constructor
7
- def initialize(status:, solution: {}, unserved: {}, total_travel_time: 0,
8
- total_idle_time: 0, total_break_time: 0, total_working_time: 0)
9
- @status = status
10
- @unserved = unserved
11
- @total_idle_time = total_idle_time
12
- @total_travel_time = total_travel_time
13
- @total_break_time = total_break_time
14
- @total_working_time = total_working_time
15
-
16
- add_solution(solution)
18
+ def initialize(data)
19
+ FIELDS.each do |field|
20
+ instance_variable_set "@#{field}", data[field]
21
+ end
22
+ add_solution(data[:solution] || {})
17
23
  end
18
24
 
19
25
  def add_solution(solution)
20
- @vehicleRoutes = {}
26
+ @vehicle_routes = {}
21
27
 
22
28
  solution.each do |vehicle_name, way_points|
23
29
  # Get all way points for this vehicle
@@ -31,16 +37,12 @@ module RoutificApi
31
37
 
32
38
  # Adds a new way point for the specified vehicle
33
39
  def add_way_point(vehicle_name, way_point)
34
- if @vehicleRoutes[vehicle_name].nil?
40
+ if @vehicle_routes[vehicle_name].nil?
35
41
  # No previous way point was added for the specified vehicle, so create a new array
36
- @vehicleRoutes[vehicle_name] = []
42
+ @vehicle_routes[vehicle_name] = []
37
43
  end
38
44
  # Adds the provided way point for the specified vehicle
39
- @vehicleRoutes[vehicle_name] << way_point
40
- end
41
-
42
- def number_of_unserved
43
- @number_of_unserved ||= unserved.count
45
+ @vehicle_routes[vehicle_name] << way_point
44
46
  end
45
47
 
46
48
  class << self
@@ -54,10 +56,6 @@ module RoutificApi
54
56
  hash.keys.each do |key|
55
57
  hash[(key.to_sym rescue key) || key] = hash.delete(key)
56
58
  end
57
-
58
- hash.delete(:num_unserved)
59
- hash.delete(:unserved) if hash[:unserved] == nil
60
-
61
59
  hash
62
60
  end
63
61
  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,20 @@
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
+ :id, :break, :start, :end, :in_transit, # breaks
8
+ :late_by # visit lateness
9
+ ]
10
+
11
+ attr_reader *FIELDS
5
12
 
6
13
  # Constructor
7
14
  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']
15
+ FIELDS.each do |field|
16
+ instance_variable_set "@#{field}", options[field.to_s]
17
+ end
12
18
  end
13
19
  end
14
20
  end
data/lib/util.rb ADDED
@@ -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.2
4
+ version: 1.7.1
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: 2017-06-26 00:00:00.000000000 Z
11
+ date: 2017-09-22 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rest-client
@@ -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,19 +81,22 @@ dependencies:
82
81
  - !ruby/object:Gem::Version
83
82
  version: '0.11'
84
83
  description: Gem to use Routific API
85
- email: andre@routific.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
@@ -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.6.13
120
122
  signing_key:
121
123
  specification_version: 4
122
124
  summary: routific API