Skip to content

Commit

Permalink
Merge pull request #65 from PLD-Agile/63-befe-add-delivery-time-and-d…
Browse files Browse the repository at this point in the history
…isplay-it

63 befe add delivery time and display it
  • Loading branch information
cfstcyr authored Nov 14, 2023
2 parents 93fa7b6 + 71e15c4 commit be296d8
Show file tree
Hide file tree
Showing 15 changed files with 511 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from typing import Optional
from uuid import UUID

from src.models.tour import DeliveryID, TourID
from src.services.command.abstract_command import AbstractCommand
from src.services.tour.tour_service import TourService


class UpdateDeliveryRequestDeliveryMan(AbstractCommand):
__delivery_request_id: DeliveryID
__tour_id: TourID
__delivery_man_id: UUID
__previous_delivery_man_id: Optional[UUID] = None

def __init__(
self, delivery_request_id: DeliveryID, tour_id: TourID, delivery_man_id: UUID
) -> None:
super().__init__("Ajustement du livreur d'une demande de livraison")
self.__delivery_request_id = delivery_request_id
self.__tour_id = tour_id
self.__delivery_man_id = delivery_man_id

def execute(self) -> None:
self.__previous_delivery_man_id = (
TourService.instance().update_delivery_request_delivery_man(
delivery_request_id=self.__delivery_request_id,
tour_id=self.__tour_id,
delivery_man_id=self.__delivery_man_id,
)
)

def undo(self) -> None:
if self.__previous_delivery_man_id is None:
raise Exception("Cannot undo a command that has not been executed")

TourService.instance().update_delivery_request_delivery_man(
delivery_request_id=self.__delivery_request_id,
tour_id=self.__delivery_man_id,
delivery_man_id=self.__previous_delivery_man_id,
)

self.__previous_delivery_man_id = None
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Optional

from src.models.tour import DeliveryID, TourID
from src.services.command.abstract_command import AbstractCommand
from src.services.tour.tour_service import TourService


class UpdateDeliveryRequestTimeWindowCommand(AbstractCommand):
__delivery_request_id: DeliveryID
__tour_id: TourID
__time_window: int
__previous_time_window: Optional[int] = None

def __init__(
self, delivery_request_id: DeliveryID, tour_id: TourID, time_window: int
) -> None:
super().__init__("Ajustement de la fenêtre de temps d'une demande de livraison")
self.__delivery_request_id = delivery_request_id
self.__tour_id = tour_id
self.__time_window = time_window

def execute(self) -> None:
self.__previous_time_window = (
TourService.instance().update_delivery_request_time_window(
delivery_request_id=self.__delivery_request_id,
tour_id=self.__tour_id,
time_window=self.__time_window,
)
)

def undo(self) -> None:
if self.__previous_time_window is None:
raise Exception("Cannot undo a command that has not been executed")

TourService.instance().update_delivery_request_time_window(
delivery_request_id=self.__delivery_request_id,
tour_id=self.__tour_id,
time_window=self.__previous_time_window,
)

self.__previous_time_window = None
102 changes: 73 additions & 29 deletions src/services/tour/tour_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Dict, List, Optional, Tuple
from uuid import UUID

from reactivex import Observable, combine_latest
from reactivex.operators import map
Expand All @@ -7,6 +8,7 @@
from src.models.map import Position
from src.models.tour import (
ComputedTour,
Delivery,
DeliveryID,
DeliveryLocation,
DeliveryRequest,
Expand All @@ -28,12 +30,14 @@
class TourService(Singleton):
__tour_requests: BehaviorSubject[Dict[TourID, TourRequest]]
__computed_tours: BehaviorSubject[Dict[TourID, ComputedTour]]
__selected_delivery_request: BehaviorSubject[Optional[DeliveryRequest]]
__selected_delivery: BehaviorSubject[Optional[Delivery]]

def __init__(self) -> None:
self.__tour_requests = BehaviorSubject({})
self.__computed_tours = BehaviorSubject({})
self.__selected_delivery_request = BehaviorSubject(None)
self.__selected_delivery = BehaviorSubject(None)

self.__tour_requests.subscribe(lambda _: self.compute_tours())

@property
def tour_requests(self) -> Observable[Dict[TourID, TourRequest]]:
Expand All @@ -42,9 +46,9 @@ def tour_requests(self) -> Observable[Dict[TourID, TourRequest]]:
@property
def tour_requests_delivery_locations(
self,
) -> Observable[Tuple[DeliveryLocation, List[DeliveryLocation]]]:
) -> Observable[Tuple[Delivery, List[DeliveryLocation]]]:
return combine_latest(
self.__selected_delivery_request,
self.__selected_delivery,
self.__tour_requests,
).pipe(
map(
Expand Down Expand Up @@ -80,19 +84,17 @@ def all_tours(self) -> Observable[List[Tour]]:
)

def clear(self) -> None:
self.__tour_requests.on_next([])
self.__computed_tours.on_next([])
self.__tour_requests.on_next({})
self.__computed_tours.on_next({})

def get_tour_requests(self) -> List[TourRequest]:
return self.__tour_requests.value

def get_computed_tours(self) -> List[ComputedTour]:
return self.__computed_tours.value

def select_delivery_request(
self, delivery_request: Optional[DeliveryRequest]
) -> None:
self.__selected_delivery_request.on_next(delivery_request)
def select_delivery(self, delivery: Optional[Delivery]) -> None:
self.__selected_delivery.on_next(delivery)

def add_delivery_request(
self, position: Position, time_window: int, tour_id: TourID
Expand All @@ -104,16 +106,7 @@ def add_delivery_request(
time_window (int): Time window for the delivery
tour_id (TourID): ID of the tour to add the delivery to (same as DeliveryMan ID)
"""
tour_request = self.__tour_requests.value.get(tour_id)

if not tour_request:
tour_request = TourRequest(
id=tour_id,
deliveries={},
delivery_man=DeliveryManService.instance().get_delivery_man(tour_id),
color=COLORS[len(self.__tour_requests.value) % len(COLORS)],
)
self.__tour_requests.value[tour_request.id] = tour_request
tour_request = self.__get_or_create_tour_request(tour_id)

delivery_request = DeliveryRequest(
location=DeliveryLocationService.instance().find_delivery_location_from_position(
Expand All @@ -126,9 +119,6 @@ def add_delivery_request(

self.__tour_requests.on_next(self.__tour_requests.value)

# TODO: review how to compute tours when adding a delivery request
self.compute_tours()

return delivery_request

def remove_delivery_request(
Expand All @@ -155,16 +145,54 @@ def remove_delivery_request(

self.__tour_requests.on_next(self.__tour_requests.value)

if self.__selected_delivery_request.value == tour_request:
self.__selected_delivery_request.on_next(None)

# TODO: review how to compute tours when adding a delivery request
self.compute_tours()
if self.__selected_delivery.value == tour_request:
self.__selected_delivery.on_next(None)

return delivery_request

def update_delivery_request_time_window(
self, delivery_request_id: DeliveryID, tour_id: TourID, time_window: int
) -> int:
tour_request = self.__tour_requests.value[tour_id]
delivery_request = tour_request.deliveries[delivery_request_id]

if delivery_request.time_window == time_window:
return

previous_time_window = delivery_request.time_window
delivery_request.time_window = time_window

self.__tour_requests.on_next(self.__tour_requests.value)

return previous_time_window

def update_delivery_request_delivery_man(
self, delivery_request_id: DeliveryID, tour_id: TourID, delivery_man_id: UUID
) -> UUID:
if tour_id == delivery_man_id:
return

tour_request = self.__tour_requests.value[tour_id]
delivery_request = tour_request.deliveries[delivery_request_id]

previous_delivery_man_id = tour_request.delivery_man.id

del tour_request.deliveries[delivery_request_id]

self.__get_or_create_tour_request(delivery_man_id).deliveries[
delivery_request_id
] = delivery_request

self.__tour_requests.on_next(self.__tour_requests.value)

return previous_delivery_man_id

def compute_tours(self) -> None:
"""Compute the tours and publish the update."""
if len(self.__tour_requests.value) == 0:
self.__computed_tours.on_next({})
return

map = MapService.instance().get_map()

tours_intersection_ids = {
Expand All @@ -175,7 +203,9 @@ def compute_tours(self) -> None:
computed_tours = {
id: TourTimeComputingService.instance().get_computed_tour_from_route_ids(
self.__tour_requests.value[id], tour_intersection_ids
) if tour_intersection_ids else None
)
if tour_intersection_ids
else None
for (id, tour_intersection_ids) in tours_intersection_ids.items()
}

Expand Down Expand Up @@ -203,3 +233,17 @@ def load_tours(self, path: str) -> None:
path (str): Path to the file
"""
self.__computed_tours.on_next(TourSavingService.instance().load_tours(path))

def __get_or_create_tour_request(self, tour_id: TourID) -> Tour:
tour_request = self.__tour_requests.value.get(tour_id)

if not tour_request:
tour_request = TourRequest(
id=tour_id,
deliveries={},
delivery_man=DeliveryManService.instance().get_delivery_man(tour_id),
color=COLORS[len(self.__tour_requests.value) % len(COLORS)],
)
self.__tour_requests.value[tour_request.id] = tour_request

return tour_request
11 changes: 8 additions & 3 deletions src/services/tour/tour_time_computing_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ def get_computed_tour_from_route_ids(

return ComputedTour.create_from_request(
tour_request=tour_request,
deliveries={delivery.id: delivery for delivery in self.__compute_time_for_deliveries(tour_request, segment_route)},
deliveries={
delivery.id: delivery
for delivery in self.__compute_time_for_deliveries(
tour_request, segment_route
)
},
route=segment_route,
)

Expand Down Expand Up @@ -59,8 +64,8 @@ def __compute_time_for_deliveries(
if travel_time < time_window_start:
travel_time = time_window_start

if travel_time > time_window_start + Config.TIME_WINDOW_SIZE:
raise Exception("Delivery time window exceeded")
# if travel_time > time_window_start + Config.TIME_WINDOW_SIZE:
# raise Exception("Delivery time window exceeded")

computed_deliveries.append(
ComputedDelivery.create_from_request(
Expand Down
Loading

0 comments on commit be296d8

Please sign in to comment.