|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +# gem 'grape', '=1.0.1' |
| 4 | + |
| 5 | +require 'grape' |
| 6 | +require 'ruby-prof' |
| 7 | +require 'hashie' |
| 8 | + |
| 9 | +class API < Grape::API |
| 10 | + # include Grape::Extensions::Hash::ParamBuilder |
| 11 | + # include Grape::Extensions::Hashie::Mash::ParamBuilder |
| 12 | + |
| 13 | + rescue_from do |e| |
| 14 | + warn "\n\n#{e.class} (#{e.message}):\n " + e.backtrace.join("\n ") + "\n\n" |
| 15 | + end |
| 16 | + |
| 17 | + prefix :api |
| 18 | + version 'v1', using: :path |
| 19 | + content_type :json, 'application/json; charset=UTF-8' |
| 20 | + default_format :json |
| 21 | + |
| 22 | + def self.vrp_request_timewindow(this) |
| 23 | + this.optional(:id, types: String) |
| 24 | + this.optional(:start, types: [String, Float, Integer]) |
| 25 | + this.optional(:end, types: [String, Float, Integer]) |
| 26 | + this.optional(:day_index, type: Integer, values: 0..6) |
| 27 | + this.at_least_one_of :start, :end, :day_index |
| 28 | + end |
| 29 | + |
| 30 | + def self.vrp_request_indice_range(this) |
| 31 | + this.optional(:start, type: Integer) |
| 32 | + this.optional(:end, type: Integer) |
| 33 | + end |
| 34 | + |
| 35 | + def self.vrp_request_point(this) |
| 36 | + this.requires(:id, type: String, allow_blank: false) |
| 37 | + this.optional(:location, type: Hash, allow_blank: false) do |
| 38 | + requires(:lat, type: Float, allow_blank: false) |
| 39 | + requires(:lon, type: Float, allow_blank: false) |
| 40 | + end |
| 41 | + end |
| 42 | + |
| 43 | + def self.vrp_request_unit(this) |
| 44 | + this.requires(:id, type: String, allow_blank: false) |
| 45 | + this.optional(:label, type: String) |
| 46 | + this.optional(:counting, type: Boolean) |
| 47 | + end |
| 48 | + |
| 49 | + def self.vrp_request_activity(this) |
| 50 | + this.optional(:duration, types: [String, Float, Integer]) |
| 51 | + this.optional(:additional_value, type: Integer) |
| 52 | + this.optional(:setup_duration, types: [String, Float, Integer]) |
| 53 | + this.optional(:late_multiplier, type: Float) |
| 54 | + this.optional(:timewindow_start_day_shift_number, documentation: { hidden: true }, type: Integer) |
| 55 | + this.requires(:point_id, type: String, allow_blank: false) |
| 56 | + this.optional(:timewindows, type: Array) do |
| 57 | + API.vrp_request_timewindow(self) |
| 58 | + end |
| 59 | + end |
| 60 | + |
| 61 | + def self.vrp_request_quantity(this) |
| 62 | + this.optional(:id, type: String) |
| 63 | + this.requires(:unit_id, type: String, allow_blank: false) |
| 64 | + this.optional(:value, type: Float) |
| 65 | + end |
| 66 | + |
| 67 | + def self.vrp_request_capacity(this) |
| 68 | + this.optional(:id, type: String) |
| 69 | + this.requires(:unit_id, type: String, allow_blank: false) |
| 70 | + this.requires(:limit, type: Float, allow_blank: false) |
| 71 | + this.optional(:initial, type: Float) |
| 72 | + this.optional(:overload_multiplier, type: Float) |
| 73 | + end |
| 74 | + |
| 75 | + def self.vrp_request_vehicle(this) |
| 76 | + this.requires(:id, type: String, allow_blank: false) |
| 77 | + this.optional(:cost_fixed, type: Float) |
| 78 | + this.optional(:cost_distance_multiplier, type: Float) |
| 79 | + this.optional(:cost_time_multiplier, type: Float) |
| 80 | + |
| 81 | + this.optional :router_dimension, type: String, values: %w[time distance] |
| 82 | + this.optional(:skills, type: Array[Array[String]]) |
| 83 | + |
| 84 | + this.optional(:unavailable_work_day_indices, type: Array[Integer]) |
| 85 | + |
| 86 | + this.optional(:free_approach, type: Boolean) |
| 87 | + this.optional(:free_return, type: Boolean) |
| 88 | + |
| 89 | + this.optional(:start_point_id, type: String) |
| 90 | + this.optional(:end_point_id, type: String) |
| 91 | + this.optional(:capacities, type: Array) do |
| 92 | + API.vrp_request_capacity(self) |
| 93 | + end |
| 94 | + |
| 95 | + this.optional(:sequence_timewindows, type: Array) do |
| 96 | + API.vrp_request_timewindow(self) |
| 97 | + end |
| 98 | + end |
| 99 | + |
| 100 | + def self.vrp_request_service(this) |
| 101 | + this.requires(:id, type: String, allow_blank: false) |
| 102 | + this.optional(:priority, type: Integer, values: 0..8) |
| 103 | + this.optional(:exclusion_cost, type: Integer) |
| 104 | + |
| 105 | + this.optional(:visits_number, type: Integer, coerce_with: ->(val) { val.to_i.positive? && val.to_i }, default: 1, allow_blank: false) |
| 106 | + |
| 107 | + this.optional(:unavailable_visit_indices, type: Array[Integer]) |
| 108 | + this.optional(:unavailable_visit_day_indices, type: Array[Integer]) |
| 109 | + |
| 110 | + this.optional(:minimum_lapse, type: Float) |
| 111 | + this.optional(:maximum_lapse, type: Float) |
| 112 | + |
| 113 | + this.optional(:sticky_vehicle_ids, type: Array[String]) |
| 114 | + this.optional(:skills, type: Array[String]) |
| 115 | + |
| 116 | + this.optional(:type, type: Symbol) |
| 117 | + this.optional(:activity, type: Hash) do |
| 118 | + API.vrp_request_activity(self) |
| 119 | + end |
| 120 | + this.optional(:quantities, type: Array) do |
| 121 | + API.vrp_request_quantity(self) |
| 122 | + end |
| 123 | + end |
| 124 | + |
| 125 | + def self.vrp_request_configuration(this) |
| 126 | + this.optional(:preprocessing, type: Hash) do |
| 127 | + API.vrp_request_preprocessing(self) |
| 128 | + end |
| 129 | + this.optional(:resolution, type: Hash) do |
| 130 | + API.vrp_request_resolution(self) |
| 131 | + end |
| 132 | + this.optional(:restitution, type: Hash) do |
| 133 | + API.vrp_request_restitution(self) |
| 134 | + end |
| 135 | + this.optional(:schedule, type: Hash) do |
| 136 | + API.vrp_request_schedule(self) |
| 137 | + end |
| 138 | + end |
| 139 | + |
| 140 | + def self.vrp_request_partition(this) |
| 141 | + this.requires(:method, type: String, values: %w[hierarchical_tree balanced_kmeans]) |
| 142 | + this.optional(:metric, type: Symbol) |
| 143 | + this.optional(:entity, type: Symbol, values: %i[vehicle work_day], coerce_with: ->(value) { value.to_sym }) |
| 144 | + this.optional(:threshold, type: Integer) |
| 145 | + end |
| 146 | + |
| 147 | + def self.vrp_request_preprocessing(this) |
| 148 | + this.optional(:max_split_size, type: Integer) |
| 149 | + this.optional(:partition_method, type: String, documentation: { hidden: true }) |
| 150 | + this.optional(:partition_metric, type: Symbol, documentation: { hidden: true }) |
| 151 | + this.optional(:kmeans_centroids, type: Array[Integer]) |
| 152 | + this.optional(:cluster_threshold, type: Float) |
| 153 | + this.optional(:force_cluster, type: Boolean) |
| 154 | + this.optional(:prefer_short_segment, type: Boolean) |
| 155 | + this.optional(:neighbourhood_size, type: Integer) |
| 156 | + this.optional(:partitions, type: Array) do |
| 157 | + API.vrp_request_partition(self) |
| 158 | + end |
| 159 | + this.optional(:first_solution_strategy, type: Array[String]) |
| 160 | + end |
| 161 | + |
| 162 | + def self.vrp_request_resolution(this) |
| 163 | + this.optional(:duration, type: Integer, allow_blank: false) |
| 164 | + this.optional(:iterations, type: Integer, allow_blank: false) |
| 165 | + this.optional(:iterations_without_improvment, type: Integer, allow_blank: false) |
| 166 | + this.optional(:stable_iterations, type: Integer, allow_blank: false) |
| 167 | + this.optional(:stable_coefficient, type: Float, allow_blank: false) |
| 168 | + this.optional(:initial_time_out, type: Integer, allow_blank: false, documentation: { hidden: true }) |
| 169 | + this.optional(:minimum_duration, type: Integer, allow_blank: false) |
| 170 | + this.optional(:time_out_multiplier, type: Integer) |
| 171 | + this.optional(:vehicle_limit, type: Integer) |
| 172 | + this.optional(:solver_parameter, type: Integer, documentation: { hidden: true }) |
| 173 | + this.optional(:solver, type: Boolean, default: true) |
| 174 | + this.optional(:same_point_day, type: Boolean) |
| 175 | + this.optional(:allow_partial_assignment, type: Boolean, default: true) |
| 176 | + this.optional(:split_number, type: Integer) |
| 177 | + this.optional(:evaluate_only, type: Boolean) |
| 178 | + this.optional(:several_solutions, type: Integer, allow_blank: false, default: 1) |
| 179 | + this.optional(:batch_heuristic, type: Boolean, default: false) |
| 180 | + this.optional(:variation_ratio, type: Integer) |
| 181 | + this.optional(:repetition, type: Integer, documentation: { hidden: true }) |
| 182 | + this.at_least_one_of :duration, :iterations, :iterations_without_improvment, :stable_iterations, :stable_coefficient, :initial_time_out, :minimum_duration |
| 183 | + this.mutually_exclusive :initial_time_out, :minimum_duration |
| 184 | + end |
| 185 | + |
| 186 | + def self.vrp_request_restitution(this) |
| 187 | + this.optional(:geometry, type: Boolean) |
| 188 | + this.optional(:geometry_polyline, type: Boolean) |
| 189 | + this.optional(:intermediate_solutions, type: Boolean) |
| 190 | + this.optional(:csv, type: Boolean) |
| 191 | + this.optional(:allow_empty_result, type: Boolean) |
| 192 | + end |
| 193 | + |
| 194 | + def self.vrp_request_schedule(this) |
| 195 | + this.optional(:range_indices, type: Hash) do |
| 196 | + API.vrp_request_indice_range(self) |
| 197 | + end |
| 198 | + this.optional(:unavailable_indices, type: Array[Integer]) |
| 199 | + end |
| 200 | + |
| 201 | + params do |
| 202 | + optional(:vrp, type: Hash, documentation: { param_type: 'body' }) do |
| 203 | + optional(:name, type: String) |
| 204 | + |
| 205 | + optional(:points, type: Array) do |
| 206 | + API.vrp_request_point(self) |
| 207 | + end |
| 208 | + |
| 209 | + optional(:units, type: Array) do |
| 210 | + API.vrp_request_unit(self) |
| 211 | + end |
| 212 | + |
| 213 | + requires(:vehicles, type: Array) do |
| 214 | + API.vrp_request_vehicle(self) |
| 215 | + end |
| 216 | + |
| 217 | + optional(:services, type: Array, allow_blank: false) do |
| 218 | + API.vrp_request_service(self) |
| 219 | + end |
| 220 | + |
| 221 | + optional(:configuration, type: Hash) do |
| 222 | + API.vrp_request_configuration(self) |
| 223 | + end |
| 224 | + end |
| 225 | + end |
| 226 | + post '/' do |
| 227 | + 'hello' |
| 228 | + end |
| 229 | +end |
| 230 | +puts Grape::VERSION |
| 231 | + |
| 232 | +options = { |
| 233 | + method: 'POST', |
| 234 | + params: JSON.parse(File.read('benchmark/resource/vrp_example.json')) |
| 235 | +} |
| 236 | + |
| 237 | +env = Rack::MockRequest.env_for('/api/v1', options) |
| 238 | + |
| 239 | +start = Time.now |
| 240 | +result = RubyProf.profile do |
| 241 | + API.call env |
| 242 | +end |
| 243 | +puts Time.now - start |
| 244 | +printer = RubyProf::FlatPrinter.new(result) |
| 245 | +File.open('test_prof.out', 'w+') { |f| printer.print(f, {}) } |
0 commit comments