dscf-marketplace 0.6.4 → 0.6.5
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
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d395ee1a2d1134af4a10cf72e0591583e898e507cf94432d62849c992d888ec
|
|
4
|
+
data.tar.gz: 4cb118146a4759d47d069d34afd0aa4b857329c4eaa28509bb62b35301c9a7b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d82bd17842e98ef5880bdf7b1396722346b1cb60722ab4cb1db52e21ca1680b97b2766d24d1f8bb7c3df70c080a17dd72f2a08d8bd67e8a0ebf88bae10ebe42
|
|
7
|
+
data.tar.gz: 6d6793f1ca44cb0976147056bcfccf23c88720109f04d7590460ccf81baba4dba272bd06423f054e7a759fbc80581e2e36415a5bca853af7c27fddb750c17028
|
|
@@ -36,8 +36,8 @@ module Dscf::Marketplace
|
|
|
36
36
|
create_delivery_stops_and_items(delivery_order, orders, pickup_address)
|
|
37
37
|
associate_orders_with_delivery(delivery_order, orders)
|
|
38
38
|
|
|
39
|
-
# Optimize route
|
|
40
|
-
|
|
39
|
+
# Optimize route using Gebeta Maps
|
|
40
|
+
RouteOptimizationService.new(delivery_order).optimize!
|
|
41
41
|
|
|
42
42
|
delivery_order
|
|
43
43
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "httparty"
|
|
2
|
+
|
|
3
|
+
module Dscf
|
|
4
|
+
module Marketplace
|
|
5
|
+
class GebetaService
|
|
6
|
+
include HTTParty
|
|
7
|
+
base_uri "https://mapapi.gebeta.app/api"
|
|
8
|
+
debug_output $stdout if Rails.env.development?
|
|
9
|
+
|
|
10
|
+
def initialize(api_key: ENV["GEBETA_MAPS_API_KEY"])
|
|
11
|
+
@api_key = api_key
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Traveling Salesperson Solver
|
|
15
|
+
# @param locations [Array<Array>] Array of coordinate pairs [[lat, lng], [lat, lng], ...]
|
|
16
|
+
# Start point is assumed to be the first element.
|
|
17
|
+
def tsp(locations)
|
|
18
|
+
# Gebeta TSS API expects a custom string format: [{lat,lon},{lat,lon},...]
|
|
19
|
+
# Example: [{8.9,38.7},{9.0,38.8}] - Literal curly braces, comma sep lat/lon
|
|
20
|
+
|
|
21
|
+
custom_payload = "[" + locations.map { |loc| "{#{loc[0]},#{loc[1]}}" }.join(",") + "]"
|
|
22
|
+
|
|
23
|
+
# We pass this string directly to the json query param.
|
|
24
|
+
# HTTParty will encode the characters (e.g., { becomes %7B), which is expected.
|
|
25
|
+
response = self.class.get("/route/tss", query: {
|
|
26
|
+
apiKey: @api_key,
|
|
27
|
+
json: custom_payload
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if response.success?
|
|
31
|
+
response.parsed_response
|
|
32
|
+
else
|
|
33
|
+
raise StandardError, "Gebeta API Error: #{response.code} - #{response.body}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class RouteOptimizationService
|
|
4
|
+
attr_reader :delivery_order
|
|
5
|
+
|
|
6
|
+
def initialize(delivery_order)
|
|
7
|
+
@delivery_order = delivery_order
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def optimize!
|
|
11
|
+
return unless delivery_order.pickup_address && delivery_order.delivery_stops.any?
|
|
12
|
+
|
|
13
|
+
# 1. Collect Coordinates
|
|
14
|
+
# Format: [[pickup_lat, pickup_lng], [stop1_lat, stop1_lng], ...]
|
|
15
|
+
pickup_coords = [delivery_order.pickup_address.latitude.to_f, delivery_order.pickup_address.longitude.to_f]
|
|
16
|
+
|
|
17
|
+
# We map stops to their dropoff coordinates.
|
|
18
|
+
# We need to keep track of the mapping between original index and stop ID to re-order later.
|
|
19
|
+
stops = delivery_order.delivery_stops.includes(:dropoff_address).to_a
|
|
20
|
+
stop_coords = stops.map { |stop| [stop.dropoff_address.latitude.to_f, stop.dropoff_address.longitude.to_f] }
|
|
21
|
+
|
|
22
|
+
all_locations = [pickup_coords] + stop_coords
|
|
23
|
+
|
|
24
|
+
# 2. Call Gebeta API
|
|
25
|
+
response = GebetaService.new.tsp(all_locations)
|
|
26
|
+
|
|
27
|
+
# 3. Process Response
|
|
28
|
+
|
|
29
|
+
# New Response Format:
|
|
30
|
+
# {
|
|
31
|
+
# "best_order": [
|
|
32
|
+
# {"lat":..., "lon":...}, # Start (Pickup)
|
|
33
|
+
# {"lat":..., "original_index": 2}, # Stop A
|
|
34
|
+
# {"lat":..., "original_index": 1} # Stop B
|
|
35
|
+
# ],
|
|
36
|
+
# "total_distance": 13.474, # In KM? Diagnostic showed 13.474 for small distance. Need to verify unit.
|
|
37
|
+
# "time_taken": 1033.263,
|
|
38
|
+
# "Direction": [[lat,lon], ...]
|
|
39
|
+
# }
|
|
40
|
+
|
|
41
|
+
# 'best_order' includes the start point at index 0 (usually without original_index or it's 0/1 based?)
|
|
42
|
+
# Based on diagnostic: "original_index": 2 for the 3rd point (index 2).
|
|
43
|
+
# So original_index is 0-based index from the input array.
|
|
44
|
+
|
|
45
|
+
best_order = response["best_order"]
|
|
46
|
+
|
|
47
|
+
# Filter out the pickup location (which should be the first one, or original_index == 0)
|
|
48
|
+
# We only want to re-sequence the STOPS.
|
|
49
|
+
# Stops in 'stops' array correspond to input indices 1..N.
|
|
50
|
+
|
|
51
|
+
ordered_stops_data = best_order.select { |node| node["original_index"].to_i > 0 }
|
|
52
|
+
|
|
53
|
+
ActiveRecord::Base.transaction do
|
|
54
|
+
ordered_stops_data.each_with_index do |node, seq_num|
|
|
55
|
+
original_idx = node["original_index"]
|
|
56
|
+
# stops array is 0-indexed, corresponding to input indices 1, 2, 3...
|
|
57
|
+
# If original_idx is 1, it means stops[0]. If 2, stops[1].
|
|
58
|
+
stop = stops[original_idx - 1]
|
|
59
|
+
|
|
60
|
+
if stop
|
|
61
|
+
stop.update!(sequence_number: seq_num + 1)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Update DeliveryOrder metrics
|
|
66
|
+
# Check units:
|
|
67
|
+
# time_taken: 1033.263 (likely seconds ~ 17 mins for decent distance).
|
|
68
|
+
# total_distance: 13.474. If this is KM, it's reasonable. (Distance between 9.02,38.80 and 9.028,38.75 is ~ small).
|
|
69
|
+
# If meters, 13 meters is too small. 1000 seconds for 13 meters is wrong.
|
|
70
|
+
# So total_distance is likely KM.
|
|
71
|
+
|
|
72
|
+
distance_km = response["total_distance"].to_f
|
|
73
|
+
|
|
74
|
+
delivery_order.update!(
|
|
75
|
+
estimated_delivery_time: Time.current + response["time_taken"].to_f.seconds,
|
|
76
|
+
estimated_delivery_price: calculate_price(distance_km * 1000), # pass meters to calc
|
|
77
|
+
optimized_route: response["Direction"]
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Placeholder pricing logic
|
|
85
|
+
def calculate_price(distance_meters)
|
|
86
|
+
base_rate = 50.0
|
|
87
|
+
km_rate = 10.0 # 10 ETB per km
|
|
88
|
+
distance_km = distance_meters / 1000.0
|
|
89
|
+
|
|
90
|
+
base_rate + (distance_km * km_rate)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dscf-marketplace
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Asrat
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-12-
|
|
10
|
+
date: 2025-12-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -472,9 +472,11 @@ files:
|
|
|
472
472
|
- app/serializers/dscf/marketplace/user_serializer.rb
|
|
473
473
|
- app/services/dscf/marketplace/delivery_order_service.rb
|
|
474
474
|
- app/services/dscf/marketplace/dispute_service.rb
|
|
475
|
+
- app/services/dscf/marketplace/gebeta_service.rb
|
|
475
476
|
- app/services/dscf/marketplace/my_resource_service.rb
|
|
476
477
|
- app/services/dscf/marketplace/rfq_response_service.rb
|
|
477
478
|
- app/services/dscf/marketplace/role_service.rb
|
|
479
|
+
- app/services/dscf/marketplace/route_optimization_service.rb
|
|
478
480
|
- config/environments/production.rb
|
|
479
481
|
- config/locales/en.yml
|
|
480
482
|
- config/locales/en.yml.backup
|