def locationize_endpoint(location): """Select Yik Yak server based on location of request""" for endpoint in get_endpoints(): min_loc = Location(endpoint["min_latitude"], endpoint["min_longitude"]) max_loc = Location(endpoint["max_latitude"], endpoint["max_longitude"]) if min_loc.longitude <= location.longitude <= max_loc.longitude: if min_loc.latitude <= location.latitude <= max_loc.latitude: settings.YIKYAK_ENDPOINT = endpoint["url"] return settings.YIKYAK_ENDPOINT = get_default_endpoint()
def test_estimate_route_properties(self, osrm): """Test to verify the route estimation works correctly""" # Defines an origin and a route that must be fulfilled origin = Location(4.678622, -74.055694) route = Route(stops=[ Stop(position=0, location=Location(4.690207, -74.044235)), Stop(position=1, location=Location(4.709022, -74.035102)) ]) # Obtains the route's distance and time and asserts expected values distance, time = OSRMService.estimate_route_properties( origin=origin, route=route, vehicle=Vehicle.CAR) self.assertEqual(int(distance), 4) self.assertEqual(time, 594)
def test_get_route(self, osrm): """Test to verify the route construction works correctly""" # Defines an origin and a destination origin = Location(4.678622, -74.055694) destination = Location(4.690207, -74.044235) # Obtains the route and asserts it is equal to the mocked value route = OSRMService.get_route(origin, destination) self.assertEqual( route.stops, Route(stops=[ Stop(position=0, location=origin), Stop(position=1, location=destination) ]).stops)
def __init__(self, org_id, API_key, loc_id=None): self.API_key = API_key self.org_id = org_id self.loc_id = loc_id self.header = {'Authorization': f'Access-Token {API_key}'} self.url = 'https://api.robinpowered.com/v1.0' self.organization = Organization(self) self.location = Location(self) self.spaces = Spaces(self)
def test_courier_busy_event(self, *args): """Test to verify the mechanics of how the dispatcher sets a courier to busy""" # Constants initial_time = hour_to_sec(14) courier_id = 14 time_delta = min_to_sec(10) on_time = time(14, 0, 0) off_time = time(15, 0, 0) service_time = min_to_sec(7) # Verifies 2 test cases for how the courier transitions to being busy # For each test case, assert the courier starts in a set and ends up in the busy set # Test 1: courier starts dropping off state env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(dispatcher=dispatcher, env=env, courier_id=courier_id, on_time=on_time, off_time=off_time) env.process(courier._dropping_off_state( orders={ 21: Order(drop_off_service_time=service_time, ready_time=time(12, 20, 0)) } )) env.run(until=initial_time + time_delta) self.assertEqual(courier.condition, 'dropping_off') self.assertEqual(dispatcher.dropping_off_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.idle_couriers, {}) # Test 2: courier start moving state env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( dispatcher=dispatcher, env=env, courier_id=courier_id, location=Location(lat=4.690296, lng=-74.043929), on_time=on_time, off_time=off_time ) env.process(courier._moving_state(destination=Location(lat=4.689697, lng=-74.055495))) env.run(until=initial_time + time_delta) self.assertEqual(courier.condition, 'moving') self.assertEqual(dispatcher.moving_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.idle_couriers, {})
def parse_world(self, file_name: str) -> NoReturn: with open(file_name, 'rb') as f: rows, cols, drone_count, turns, max_payload = [ int(x) for x in f.readline().split() ] self.grid = Location(rows, cols) self.turns = turns self.max_payload = max_payload product_types_count = int(f.readline()) self.products = [int(x) for x in f.readline().split()] assert (len(self.products) == product_types_count) warehouse_count = int(f.readline()) for wi in range(warehouse_count): row, col = [int(x) for x in f.readline().split()] stocks = [int(x) for x in f.readline().split()] self.warehouses.append(Warehouse(Location(row, col), stocks)) order_count = int(f.readline()) for ci in range(order_count): row, col = [int(x) for x in f.readline().split()] order_items_count = int(f.readline()) items = [int(x) for x in f.readline().split()] cart = {} for item in items: cart[item] = cart.setdefault(item, 0) + 1 self.orders.append(Order(Location(row, col), cart)) self.drones = [ Drone(Location(0, 0), self.products, self.max_payload) for _ in range(drone_count) ] logger.info("World is of size %d X %d", rows, cols) logger.info("There are %d drones", drone_count) logger.info("There are %d turns", turns) logger.info("Maximum drone lift-off payload is: %d", max_payload) logger.info("There are %d drones", drone_count) logger.info("There are %d warehouses", warehouse_count) logger.info("There are %d orders", order_count) logger.debug(self.products) logger.debug(self.warehouses) logger.debug(self.orders)
def test_group_by_geohash(self): """Test to verify how the target bundle size is calculated""" # Create 3 orders to be grouped into 2 geohashes order_1 = Order(order_id=1, pick_up_at=Location(lat=4.678417, lng=-74.054725)) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.678417, lng=-74.054725)) order_3 = Order(order_id=3, pick_up_at=Location(lat=4.717045, lng=-74.036359)) groups = MyopicMatchingPolicy._group_by_geohash( orders=[order_1, order_2, order_3]) # Asserts only 2 groups are created from 3 groups self.assertEqual(len(groups), 2) self.assertIn(order_1, groups.get('d2g6dgd')) self.assertIn(order_2, groups.get('d2g6dgd')) self.assertIn(order_3, groups.get('d2g6g68'))
def __init__(self, file_name): self.warehouses = [] self.orders = [] self.products = [] self.grid = Location(0, 0) self.turns = 0 self.drones = [] self.max_payload = 0 self.time = 0 self.parse_world(file_name)
def _new_users_procedure(self, orders_info: List[Dict[str, Any]]): """Method to establish how a new user is created in the World""" for order_info in orders_info: user = User(env=self.env, dispatcher=self.dispatcher, cancellation_policy=USER_CANCELLATION_POLICIES_MAP[ settings.USER_CANCELLATION_POLICY], user_id=order_info['order_id']) user.submit_order_event( order_id=order_info['order_id'], pick_up_at=Location(lat=order_info['pick_up_lat'], lng=order_info['pick_up_lng']), drop_off_at=Location(lat=order_info['drop_off_lat'], lng=order_info['drop_off_lng']), placement_time=order_info['placement_time'], expected_drop_off_time=order_info['expected_drop_off_time'], preparation_time=order_info['preparation_time'], ready_time=order_info['ready_time']) self.users.append(user)
def execute(self, current_location: Location) -> Optional[Location]: """Execution of the Movement Evaluation Policy""" if random.random() <= settings.COURIER_MOVEMENT_PROBABILITY: current_geohash = geohash.encode(*current_location.coordinates, precision=6) geohash_neighbors = geohash.neighbors(current_geohash) destination_geohash = random.choice(geohash_neighbors) destination_coordinates = geohash.decode(destination_geohash) return Location(lat=destination_coordinates[0], lng=destination_coordinates[1]) return None
def test_get_estimations(self, osrm): """Test to verify that estimations are correctly calculated""" # Constants on_time = time(15, 0, 0) off_time = time(16, 0, 0) # Services policy = GreedyMatchingPolicy() # Create an order and a courier and have them be prospects order = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235)) courier = Courier(location=Location(4.709022, -74.035102), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR) prospects = np.array([[0, 0]]) # Obtain estimations and assert they are correctly calculated estimations = policy._get_estimations(orders=[order], couriers=[courier], prospects=prospects) self.assertEqual( round(estimations['distance'].tolist()[0], 2), round( haversine(courier.location.coordinates, order.pick_up_at.coordinates) + haversine(order.pick_up_at.coordinates, order.drop_off_at.coordinates), 2)) average_velocity = courier.vehicle.average_velocity self.assertEqual( int(estimations['time'][0]), int( haversine(courier.location.coordinates, order.pick_up_at.coordinates) / average_velocity + haversine(order.pick_up_at.coordinates, order.drop_off_at.coordinates) / average_velocity + order.pick_up_service_time + order.drop_off_service_time))
def update(self): """Update Yakarma and basecamp information""" raw = yikyakapi.get_messages(self, self.location, basecamp=True) # Check if too close to school self._get_yak_list(raw) try: self.yakarma = int(raw.json()["yakarma"]) except (KeyError, ValueError): raise ParsingResponseError("Getting Yakarma failed", raw) try: self.basecamp_set = bool(int(raw.json()["bcEligible"])) except (KeyError, ValueError): raise ParsingResponseError("Getting bcEligible failed", raw) try: latitude = float(raw.json()["bcLat"]) longitude = float(raw.json()["bcLong"]) self.basecamp_name = raw.json()["bcName"] self.basecamp_location = Location(latitude, longitude) except (KeyError, ValueError): pass
def addToInventory(self,title="",status="STOCK",authors=[],publisher="",listprice="",ourprice='',isbn="",categories=[],distributor="",location="",owner="",notes="",quantity=1,known_title=False,types='',kind_name="",kind=default_kind, extra_prices={}, tag='', num_copies=0, printlabel=False): print "GOT to addToInventory" if not(known_title): print "unknown title" #add a title the_kinds=list(Kind.select(Kind.q.kindName==kind)) kind_id = None if the_kinds: kind_id = the_kinds[0].id print 'kind id is', kind_id #print title title=title publisher=publisher #print title, publisher known_title=Title(isbn=isbn, booktitle=title, publisher=publisher,tag=" ",type=types, kindID=kind_id) print known_title for rawAuthor in authors: author = rawAuthor.encode("utf8", "backslashreplace") theAuthors = Author.selectBy(authorName=author) theAuthorsList = list(theAuthors) if len(theAuthorsList) == 1: known_title.addAuthor(theAuthorsList[0]) elif len(theAuthorsList) == 0: a = Author(authorName=author) known_title.addAuthor(a) else: # We should SQLDataCoherenceLost here print "mmm... looks like you have multiple author of the sama name in your database..." for category in categories: Category(categoryName=category.encode("utf8", "backslashreplace"),title=known_title) the_locations=list(Location.select(Location.q.locationName==location)) location_id=1 if the_locations: location_id = the_locations[0].id if not ourprice: ourprice=listprice for i in range(int(quantity)): #print "book loop" b=Book(title=known_title,status=status.encode("utf8", "backslashreplace"), distributor=distributor.encode('ascii', "backslashreplace"),listprice=listprice, ourprice=ourprice, location=location_id,owner=owner.encode("utf8", "backslashreplace"),notes=notes.encode("utf8", "backslashreplace"),consignmentStatus="")
def get_route(cls, origin: Location, destination: Location) -> Route: """Method to obtain a movement route using docker-mounted OSRM""" lat_0, lng_0 = origin.coordinates lat_1, lng_1 = destination.coordinates url = cls.URL.format(lng_0=lng_0, lat_0=lat_0, lng_1=lng_1, lat_1=lat_1) try: response = requests.get(url, timeout=5) if response and response.status_code in [requests.codes.ok, requests.codes.no_content]: response_data = response.json() steps = response_data.get('routes', [])[0].get('legs', [])[0].get('steps', []) stops = [] for ix, step in enumerate(steps): lng, lat = step.get('maneuver', {}).get('location', []) stop = Stop( location=Location(lat=lat, lng=lng), position=ix ) stops.append(stop) return Route(stops=stops) except: logging.exception('Exception captured in OSRMService.get_route. Check Docker.') return Route( stops=[ Stop( location=origin, position=0 ), Stop( location=destination, position=1 ) ] )
def _new_couriers_procedure(self, couriers_info: List[Dict[str, Any]]): """Method to establish how a new courier is created in the World""" for courier_info in couriers_info: courier = Courier( env=self.env, dispatcher=self.dispatcher, acceptance_policy=COURIER_ACCEPTANCE_POLICIES_MAP[ settings.COURIER_ACCEPTANCE_POLICY], movement_evaluation_policy= COURIER_MOVEMENT_EVALUATION_POLICIES_MAP[ settings.COURIER_MOVEMENT_EVALUATION_POLICY], movement_policy=COURIER_MOVEMENT_POLICIES_MAP[ settings.COURIER_MOVEMENT_POLICY], courier_id=courier_info['courier_id'], vehicle=Vehicle.from_label(label=courier_info['vehicle']), location=Location(lat=courier_info['on_lat'], lng=courier_info['on_lng']), on_time=courier_info['on_time'], off_time=courier_info['off_time']) self.couriers.append(courier)
def __init__(self, raw, user): """Initialize comment from raw JSON dict and user""" super(Yak, self).__init__(raw, user) latitude = raw["latitude"] longitude = raw["longitude"] self.comments = raw["comments"] self.hide_pin = bool(raw["hidePin"]) self.loaded = raw["messageID"][:2] == "R/" self.location = Location(latitude, longitude) self.message = raw["message"] self.type = int(raw["type"]) # If Yak is not fully loaded try: self.gmt = raw["gmt"] self.read_only = bool(raw["readOnly"]) self.score = raw["score"] except KeyError: self.gmt = None self.read_only = None self.score = None # If handle is present try: self.handle = raw["handle"] except KeyError: self.handle = None # If Yak contains a picture if self.type == YAK_TYPE_PICTURE: self.url = raw["url"] self.expand_in_feed = bool(raw["expandInFeed"]) else: self.expand_in_feed = None self.thumbnail_url = None self.url = None
def test_myopic_matching_policy_execute_mip_matcher(self, osrm): """Test to verify how the optimization model is solved with a MIP approach""" # Constants env_time = hour_to_sec(12) + min_to_sec(20) on_time = time(8, 0, 0) off_time = time(16, 0, 0) random.seed(45) # Orders order_1 = Order(order_id=1, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.681694, lng=-74.044811), ready_time=time(12, 30, 0), expected_drop_off_time=time(12, 40, 0), pick_up_service_time=0, drop_off_service_time=0) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.695001, lng=-74.040737), ready_time=time(12, 32, 0), expected_drop_off_time=time(12, 42, 0), pick_up_service_time=0, drop_off_service_time=0) order_3 = Order(order_id=3, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.668742, lng=-74.056684), ready_time=time(12, 33, 0), expected_drop_off_time=time(12, 43, 0), pick_up_service_time=0, drop_off_service_time=0) order_4 = Order(order_id=4, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.661441, lng=-74.056955), ready_time=time(12, 34, 0), expected_drop_off_time=time(12, 44, 0), pick_up_service_time=0, drop_off_service_time=0) # Couriers courier_1 = Courier(courier_id=1, on_time=on_time, off_time=off_time, condition='idle', location=Location(lat=4.676854, lng=-74.057498)) courier_2 = Courier(courier_id=2, on_time=on_time, off_time=off_time, condition='idle', location=Location(lat=4.679408, lng=-74.052524)) courier_3 = Courier( courier_id=3, on_time=on_time, off_time=off_time, condition='picking_up', location=order_3.pick_up_at, active_route=Route(orders={order_3.order_id: order_3}, stops=[ Stop(location=order_3.pick_up_at, orders={order_3.order_id: order_3}, position=0, type=StopType.PICK_UP), Stop(location=order_3.drop_off_at, orders={order_3.order_id: order_3}, position=1, type=StopType.DROP_OFF) ]), active_stop=Stop(location=order_3.pick_up_at, orders={order_3.order_id: order_3}, position=0, type=StopType.PICK_UP)) # Get all the elements from the policy and assert their expected behavior policy = MyopicMatchingPolicy(assignment_updates=False, prospects=False, notification_filtering=False, mip_matcher=False) routes = policy._generate_routes( orders=[order_1, order_2, order_4], couriers=[courier_1, courier_2, courier_3], env_time=env_time) self.assertTrue(routes) self.assertEqual(len(routes), 2) self.assertEqual(len(routes[0].orders), 2) self.assertEqual(len(routes[1].orders), 1) prospects = policy._generate_matching_prospects( routes=routes, couriers=[courier_1, courier_2, courier_3], env_time=env_time) self.assertTrue(prospects.tolist()) self.assertEqual(len(prospects), 4), self.assertEqual(len(prospects), len(routes) * len([courier_1, courier_2])) costs = policy._generate_matching_costs( routes=routes, couriers=[courier_1, courier_2, courier_3], prospects=prospects, env_time=env_time) self.assertTrue(costs.tolist()) self.assertEqual(len(prospects), len(costs)) self.assertEqual(len(costs), 4) self.assertNotIn(0., costs) problem = MatchingProblemBuilder.build( routes=routes, couriers=[courier_1, courier_2, courier_3], prospects=prospects, costs=costs) self.assertTrue(problem) self.assertEqual(len(prospects), len(problem.prospects)) self.assertEqual(len(prospects), len(problem.matching_prospects)) self.assertEqual(len(prospects), len(problem.costs)) self.assertEqual(routes, problem.routes) self.assertEqual(problem.couriers, [courier_1, courier_2, courier_3]) self.assertNotIn(str(courier_3.courier_id), problem.matching_prospects['i']) model_builder = MIPOptimizationModelBuilder( sense='max', model_constraints=[ CourierAssignmentConstraint(), RouteAssignmentConstraint() ], optimizer='pulp') model = model_builder.build(problem) self.assertTrue(model) self.assertEqual(len(model.constraints), len(problem.routes) + len([courier_1, courier_2])) self.assertEqual(len(model.variable_set), len(problem.matching_prospects) + len(problem.routes)) solution = model.solve() self.assertTrue(solution.tolist()) self.assertEqual(len(solution), len(problem.matching_prospects) + len(problem.routes)) self.assertEqual(solution[0:len(problem.prospects)].sum(), 2) self.assertEqual(solution.sum(), 2) notifications = policy._process_solution(solution, problem, env_time) self.assertEqual(len(notifications), len(routes)) self.assertIsInstance(notifications[0].instruction, Route) self.assertIsInstance(notifications[1].instruction, Route) self.assertEqual(notifications[0].courier, courier_1) self.assertEqual(notifications[1].courier, courier_2) self.assertIn(order_1.order_id, notifications[1].instruction.orders.keys()) self.assertIn(order_4.order_id, notifications[1].instruction.orders.keys())
class TestsUser(unittest.TestCase): """Tests for the User actor class""" # Order properties to be reused order_id = 0 pick_up_at = Location(lat=4.689697, lng=-74.055495) drop_off_at = Location(lat=4.690296, lng=-74.043929) placement_time = time(12, 0, 0) expected_drop_off_time = time(12, 40, 0) preparation_time = time(12, 1, 0) ready_time = time(12, 11, 0) # Services to be reused cancellation_policy = RandomCancellationPolicy() @patch('settings.settings.USER_WAIT_TO_CANCEL', min_to_sec(5)) @patch('settings.settings.USER_CANCELLATION_PROBABILITY', 0.99) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(50)) def test_submit_cancel_order(self): """Test to verify how a user submits and decides to cancel an order""" # Constants random.seed(666) initial_time = hour_to_sec(12) time_delta = min_to_sec(10) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Create a user and have it submit an order immediately user = User(cancellation_policy=self.cancellation_policy, dispatcher=dispatcher, env=env) user.submit_order_event( order_id=self.order_id, pick_up_at=self.pick_up_at, drop_off_at=self.drop_off_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time) env.run(until=initial_time + time_delta) # Verify order is created and canceled due to a courier not being assigned self.assertTrue(user.order) self.assertIsNone(user.order.courier_id) self.assertIsNotNone(user.order.cancellation_time) self.assertEqual(dispatcher.unassigned_orders, {}) self.assertIn(self.order_id, dispatcher.canceled_orders.keys()) self.assertEqual( user.order.cancellation_time, (datetime.combine(date.today(), self.placement_time) + timedelta(seconds=settings.USER_WAIT_TO_CANCEL)).time()) self.assertEqual(user.condition, 'canceled') @patch('settings.settings.USER_WAIT_TO_CANCEL', min_to_sec(40)) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(50)) def test_submit_courier_assigned(self): """Test to verify how a user submits and order and doesn't cancel since a courier is assigned""" # Constants random.seed(666) # Services env = Environment(initial_time=hour_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Create a user, have it submit an order immediately and after some minutes, assign a courier user = User(cancellation_policy=self.cancellation_policy, dispatcher=dispatcher, env=env) user.submit_order_event( order_id=self.order_id, pick_up_at=self.pick_up_at, drop_off_at=self.drop_off_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time) env.process(TestsDispatcher.assign_courier(user, env, dispatcher)) env.run(until=hour_to_sec(13)) # Verify order is created but not canceled because a courier was assigned self.assertTrue(user.order) self.assertIsNotNone(user.order.courier_id) self.assertIsNone(user.order.cancellation_time) self.assertEqual(dispatcher.assigned_orders, {self.order_id: user.order}) self.assertEqual(dispatcher.unassigned_orders, {}) self.assertEqual(user.condition, 'waiting') @patch('settings.settings.USER_CANCELLATION_PROBABILITY', 0) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(120)) def test_submit_wait_for_order(self, *args): """Test to verify how a user submits an order but doesn't cancel even without courier, deciding to wait""" # Constants random.seed(157) # Services env = Environment(initial_time=hour_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Create a user and have it submit an order immediately user = User(cancellation_policy=self.cancellation_policy, dispatcher=dispatcher, env=env) user.submit_order_event( order_id=self.order_id, pick_up_at=self.pick_up_at, drop_off_at=self.drop_off_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time) env.run(until=hour_to_sec(13)) # Verify order is created but not canceled, disregarding the lack of a courier self.assertTrue(user.order) self.assertIsNone(user.order.courier_id) self.assertIsNone(user.order.cancellation_time) self.assertEqual(dispatcher.unassigned_orders, {self.order_id: user.order}) self.assertEqual(user.condition, 'waiting')
def test_get_prospects(self): """Test to verify how prospects are obtained""" # Constants on_time = time(14, 0, 0) off_time = time(16, 0, 0) # Services policy = GreedyMatchingPolicy() # Case 1: verify an order is prospect to a courier order = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235)) courier = Courier(location=Location(4.709022, -74.035102), on_time=on_time, off_time=off_time) prospects = policy._get_prospects(orders=[order], couriers=[courier]) self.assertEqual(prospects.tolist(), [[0, 0]]) # Case 2: verify an order is not prospect to a courier order = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235)) courier = Courier(location=Location(4.8090, -74.9351), on_time=on_time, off_time=off_time, active_route=Route(stops=[], orders={ 2: Order(), 3: Order(), 4: Order() })) prospects = policy._get_prospects(orders=[order], couriers=[courier]) self.assertEqual(prospects.tolist(), []) # Case 3: assert some orders being prospect and some not to some couriers order_1 = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235), order_id=1) order_2 = Order(pick_up_at=Location(1.178, -72.25), drop_off_at=Location(1.690207, -75.044235), order_id=2) courier_1 = Courier(location=Location(4.709022, -74.035102), on_time=on_time, off_time=off_time, courier_id=1) courier_2 = Courier(location=Location(4.709022, -74.035102), on_time=on_time, off_time=off_time, courier_id=2) prospects = policy._get_prospects(orders=[order_1, order_2], couriers=[courier_1, courier_2]) self.assertEqual(len(prospects), 2)
def test_execute(self, osrm): """Test the full functionality of the greedy matching policy""" # Constants on_time = time(7, 0, 0) off_time = time(9, 0, 0) # Services policy = GreedyMatchingPolicy() # Test 1: creates an order and two couriers. # Since the courier_2 is at the same location as the pick up, asserts it should get chosen for notification order = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235)) courier_1 = Courier(location=Location(4.709022, -74.035102), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR, condition='idle') courier_2 = Courier(location=Location(4.678622, -74.055694), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR, condition='idle') notifications, _ = policy.execute(orders=[order], couriers=[courier_1, courier_2], env_time=3) self.assertEqual(len(notifications), 1) self.assertEqual(notifications[0].courier, courier_2) self.assertIn(order, notifications[0].instruction.orders.values()) # Test 2: creates two orders and two couriers. # The courier_1 is at the same location as the pick up of order_1. # The courier_2 is at the same location as the pick up of order_2. # In this fashion, courier_1 should be selected for order_1 and courier_2 for order_2 order_1 = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235), order_id=1) order_2 = Order(pick_up_at=Location(4.690207, -74.044235), drop_off_at=Location(4.678622, -74.055694), order_id=2) courier_1 = Courier(location=Location(4.678622, -74.055694), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR, condition='idle', courier_id=1) courier_2 = Courier(location=Location(4.690207, -74.044235), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR, condition='idle', courier_id=2) notifications, _ = policy.execute(orders=[order_1, order_2], couriers=[courier_1, courier_2], env_time=4) self.assertEqual(len(notifications), 2) self.assertEqual(notifications[0].courier, courier_1) self.assertIn(order_1, notifications[0].instruction.orders.values()) self.assertEqual(notifications[1].courier, courier_2) self.assertIn(order_2, notifications[1].instruction.orders.values()) # Test 3: creates more orders than couriers to check nothing breaks order_1 = Order(pick_up_at=Location(4.678622, -74.055694), drop_off_at=Location(4.690207, -74.044235), order_id=1) order_2 = Order(pick_up_at=Location(4.690207, -74.044235), drop_off_at=Location(4.678622, -74.055694), order_id=2) courier = Courier(location=Location(4.678622, -74.055694), on_time=on_time, off_time=off_time, vehicle=Vehicle.CAR, condition='idle', courier_id=1) notifications, _ = policy.execute(orders=[order_1, order_2], couriers=[courier], env_time=5) self.assertEqual(len(notifications), 1) self.assertEqual(notifications[0].courier, courier) self.assertIn(order_1, notifications[0].instruction.orders.values())
def test_update_route(self, osrm): """Test to verify a route is updated based on canceled orders""" # Constants order_1 = Order(order_id=1, pick_up_at=Location(lat=4.567, lng=1.234), drop_off_at=Location(lat=1.234, lng=4.567)) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.567, lng=1.234), drop_off_at=Location(lat=1.234, lng=4.567)) # Test 1: define a route and some orders being canceled orders_dict = {order_1.order_id: order_1, order_2.order_id: order_2} route = Route(orders=orders_dict, stops=[ Stop(orders={order_1.order_id: order_1}, type=StopType.PICK_UP, position=0, location=order_1.pick_up_at), Stop(orders={order_2.order_id: order_2}, type=StopType.PICK_UP, position=1, location=order_2.pick_up_at), Stop(orders={order_1.order_id: order_1}, type=StopType.DROP_OFF, position=2, location=order_1.drop_off_at), Stop(orders={order_2.order_id: order_2}, type=StopType.DROP_OFF, position=3, location=order_2.drop_off_at) ]) canceled_order_ids = [1] # Update the route and assert canceled orders were removed route.update(canceled_order_ids) self.assertEqual(len(route.orders), 1) self.assertEqual(len(route.stops), 2) for stop in route.stops: self.assertNotIn(order_1.order_id, stop.orders) self.assertEqual(len(stop.orders), 1) # Test 2: define a route and all orders being canceled orders_dict = {order_1.order_id: order_1, order_2.order_id: order_2} route = Route(orders=orders_dict, stops=[ Stop(orders=orders_dict, type=StopType.PICK_UP, position=0, location=order_1.pick_up_at), Stop(orders=orders_dict, type=StopType.DROP_OFF, position=2, location=order_1.drop_off_at) ]) canceled_order_ids = [1, 2] # Update the route and assert canceled orders were removed route.update(canceled_order_ids) self.assertEqual(len(route.orders), 0) self.assertEqual(len(route.stops), 0)
class TestsDispatcher(unittest.TestCase): """Tests for the Dispatcher actor class""" # Order properties to be reused order_id = 0 pick_up_at = Location(lat=4.689697, lng=-74.055495) drop_off_at = Location(lat=4.690296, lng=-74.043929) placement_time = time(12, 0, 0) expected_drop_off_time = time(12, 40, 0) preparation_time = time(12, 1, 0) ready_time = time(12, 11, 0) # Services to be reused cancellation_policy = RandomCancellationPolicy() dispatcher_cancellation_policy = StaticCancellationPolicy() @staticmethod def assign_courier(_user: User, _env: Environment, _dispatcher: Dispatcher): """Dummy method to assign a courier""" yield _env.timeout(delay=min_to_sec(8)) _user.order.courier_id = 999 del _dispatcher.unassigned_orders[_user.order.order_id] _dispatcher.assigned_orders[_user.order.order_id] = _user.order @patch('settings.settings.USER_CANCELLATION_PROBABILITY', 0) @patch('settings.settings.USER_WAIT_TO_CANCEL', min_to_sec(10)) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(15)) def test_cancel_order_event(self): """Test to verify how the dispatcher cancels an order after certain time""" random.seed(741) # Services env = Environment(initial_time=hour_to_sec(12)) dispatcher = Dispatcher( env=env, cancellation_policy=self.dispatcher_cancellation_policy, matching_policy=DummyMatchingPolicy() ) # Create a user and have it submit an order immediately, avoiding user cancellation user = User(cancellation_policy=self.cancellation_policy, dispatcher=dispatcher, env=env) user.submit_order_event( order_id=self.order_id, pick_up_at=self.pick_up_at, drop_off_at=self.drop_off_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time ) env.run(until=hour_to_sec(14)) # Verify order is canceled by the dispatcher self.assertTrue(user.order) self.assertIsNone(user.order.courier_id) self.assertIsNotNone(user.order.cancellation_time) self.assertEqual(dispatcher.unassigned_orders, {}) self.assertIn(self.order_id, dispatcher.canceled_orders.keys()) self.assertEqual( user.order.cancellation_time, ( datetime.combine(date.today(), self.preparation_time) + timedelta(seconds=settings.DISPATCHER_WAIT_TO_CANCEL) ).time() ) self.assertEqual(user.condition, 'canceled') @patch('settings.settings.USER_CANCELLATION_PROBABILITY', 0) @patch('settings.settings.USER_WAIT_TO_CANCEL', min_to_sec(44)) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(55)) def test_order_not_canceled(self): """ Test to verify that the dispatcher doesn't cancel an order if it has a courier assigned. The user doesn't see a courier but decides not to cancel. """ random.seed(192) # Services env = Environment(initial_time=hour_to_sec(12)) dispatcher = Dispatcher( env=env, cancellation_policy=self.dispatcher_cancellation_policy, matching_policy=DummyMatchingPolicy() ) # Create a user, have it submit an order immediately and after some minutes, assign a courier. # Courier is assigned after user cancellation time has expired user = User(cancellation_policy=self.cancellation_policy, dispatcher=dispatcher, env=env) user.submit_order_event( order_id=self.order_id, pick_up_at=self.pick_up_at, drop_off_at=self.drop_off_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time ) env.process(self.assign_courier(user, env, dispatcher)) env.run(until=hour_to_sec(13)) # Verify order is created but not canceled because a courier was assigned self.assertTrue(user.order) self.assertIsNotNone(user.order.courier_id) self.assertIsNone(user.order.cancellation_time) self.assertEqual(dispatcher.assigned_orders, {self.order_id: user.order}) self.assertEqual(dispatcher.unassigned_orders, {}) self.assertEqual(user.condition, 'waiting') def test_order_submitted_event(self): """Test to verify the mechanics of the order submitted event""" # Constants initial_time = hour_to_sec(15) placement_time = time(15, 0, 0) preparation_time = time(15, 1, 0) ready_time = time(15, 15, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates an order and submits it to the dispatcher order = Order(order_id=32, placement_time=placement_time) dispatcher.order_submitted_event(order, preparation_time, ready_time) env.run(until=initial_time + 121) # Verify order properties are set and it is correctly allocated self.assertEqual(order.preparation_time, preparation_time) self.assertEqual(order.ready_time, ready_time) self.assertIn(order.order_id, dispatcher.unassigned_orders.keys()) def test_orders_picked_up_event(self): """Test to verify the mechanics of orders being picked up""" # Constants initial_time = hour_to_sec(14) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates an order and sends the picked up event order = Order(order_id=45) dispatcher.orders_picked_up_event(orders={order.order_id: order}) env.run(until=initial_time + hour_to_sec(1)) # Verify order properties are modified self.assertEqual(order.state, 'picked_up') self.assertEqual(order.pick_up_time, sec_to_time(initial_time)) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_orders_dropped_off_event(self): """Test to verify the mechanics of orders being dropped off""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(16, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates an order and sends the picked up event order = Order(order_id=45, user=User(env=env)) dispatcher.assigned_orders[order.order_id] = order courier = Courier(on_time=on_time, off_time=off_time) dispatcher.orders_dropped_off_event(orders={order.order_id: order}, courier=courier) env.run(until=initial_time + hour_to_sec(1)) # Verify order properties are modified and it is allocated correctly self.assertEqual(order.state, 'dropped_off') self.assertEqual(order.drop_off_time, sec_to_time(initial_time)) self.assertIn(order.order_id, dispatcher.fulfilled_orders.keys()) self.assertEqual(dispatcher.assigned_orders, {}) self.assertIn(order.order_id, courier.fulfilled_orders) def test_notification_accepted_event(self): """Test to verify the mechanics of a notification being accepted by a courier""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates an instruction with an order, a courier and sends the accepted event order = Order(order_id=45) instruction = Route( stops=[ Stop(orders={order.order_id: order}, position=0), Stop(orders={order.order_id: order}, position=1) ], orders={order.order_id: order} ) dispatcher.unassigned_orders[order.order_id] = order courier = Courier(dispatcher=dispatcher, env=env, courier_id=89, on_time=on_time, off_time=off_time) courier.condition = 'idle' notification = Notification( courier=courier, instruction=instruction ) dispatcher.notification_accepted_event(notification=notification, courier=courier) env.run(until=initial_time + min_to_sec(10)) # Verify order and courier properties are modified and it is allocated correctly self.assertEqual(order.state, 'in_progress') self.assertEqual(order.acceptance_time, sec_to_time(initial_time)) self.assertEqual(order.courier_id, courier.courier_id) self.assertIn(order.order_id, dispatcher.assigned_orders.keys()) self.assertIsNotNone(courier.active_route) self.assertEqual(courier.active_route, instruction) self.assertEqual(dispatcher.unassigned_orders, {}) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_notification_rejected_event(self): """Test to verify the mechanics of a notification being rejected by a courier""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates an instruction with an order, a courier and sends the rejected event order = Order(order_id=45) instruction = Route( stops=[ Stop(orders={order.order_id: order}, position=0), Stop(orders={order.order_id: order}, position=1) ], orders={order.order_id: order} ) dispatcher.unassigned_orders[order.order_id] = order courier = Courier(dispatcher=dispatcher, env=env, courier_id=89, on_time=on_time, off_time=off_time) notification = Notification( courier=courier, instruction=instruction ) dispatcher.notification_rejected_event(notification=notification, courier=courier) env.run(until=initial_time + min_to_sec(30)) # Verify order and courier properties are modified and it is allocated correctly self.assertEqual(order.state, 'unassigned') self.assertIsNone(order.acceptance_time) self.assertIsNone(order.courier_id) self.assertIn(order.order_id, dispatcher.unassigned_orders.keys()) self.assertIsNone(courier.active_route) self.assertIn(courier.courier_id, order.rejected_by) self.assertIn(order.order_id, courier.rejected_orders) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_courier_idle_event(self, *args): """Test to verifiy the mechanics of how a courier is set to idle by the dispatcher""" # Constants initial_time = hour_to_sec(14) courier_id = 85 time_delta = min_to_sec(10) random.seed(26) on_time = time(14, 0, 0) off_time = time(15, 0, 0) # Verifies 3 test cases: when the courier is busy, available or idle. # For each test case, assert the courier starts in a set and ends up in the idle set # Test 1: courier is busy env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(dispatcher=dispatcher, env=env, courier_id=courier_id, on_time=on_time, off_time=off_time) dispatcher.moving_couriers = {courier.courier_id: courier} dispatcher.courier_idle_event(courier) env.run(until=initial_time + time_delta) self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) # Test 2: courier is available env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(dispatcher=dispatcher, env=env, courier_id=courier_id, on_time=on_time, off_time=off_time) dispatcher.picking_up_couriers = {courier.courier_id: courier} dispatcher.courier_idle_event(courier) env.run(until=initial_time + time_delta) self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) # Test 3: courier is idle env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(dispatcher=dispatcher, env=env, courier_id=courier_id, on_time=on_time, off_time=off_time) dispatcher.idle_couriers = {courier.courier_id: courier} dispatcher.courier_idle_event(courier) env.run(until=initial_time + time_delta) self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) def test_courier_available_event(self): """Test to verify the mechanics of how the dispatcher sets a courier to available""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(15, 0, 0) service_time = min_to_sec(6) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier and sets it to the picking state courier = Courier(dispatcher=dispatcher, env=env, courier_id=32, on_time=on_time, off_time=off_time) dispatcher.idle_couriers = {courier.courier_id: courier} env.process(courier._picking_up_state( orders={ 21: Order(drop_off_service_time=service_time, ready_time=time(14, 20, 0)) } )) env.run(until=initial_time + min_to_sec(10)) # Verify courier properties are modified and it is allocated correctly self.assertEqual(courier.condition, 'picking_up') self.assertIn(courier.courier_id, dispatcher.picking_up_couriers.keys()) self.assertEqual(dispatcher.idle_couriers, {}) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) def test_courier_busy_event(self, *args): """Test to verify the mechanics of how the dispatcher sets a courier to busy""" # Constants initial_time = hour_to_sec(14) courier_id = 14 time_delta = min_to_sec(10) on_time = time(14, 0, 0) off_time = time(15, 0, 0) service_time = min_to_sec(7) # Verifies 2 test cases for how the courier transitions to being busy # For each test case, assert the courier starts in a set and ends up in the busy set # Test 1: courier starts dropping off state env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(dispatcher=dispatcher, env=env, courier_id=courier_id, on_time=on_time, off_time=off_time) env.process(courier._dropping_off_state( orders={ 21: Order(drop_off_service_time=service_time, ready_time=time(12, 20, 0)) } )) env.run(until=initial_time + time_delta) self.assertEqual(courier.condition, 'dropping_off') self.assertEqual(dispatcher.dropping_off_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.idle_couriers, {}) # Test 2: courier start moving state env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( dispatcher=dispatcher, env=env, courier_id=courier_id, location=Location(lat=4.690296, lng=-74.043929), on_time=on_time, off_time=off_time ) env.process(courier._moving_state(destination=Location(lat=4.689697, lng=-74.055495))) env.run(until=initial_time + time_delta) self.assertEqual(courier.condition, 'moving') self.assertEqual(dispatcher.moving_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.idle_couriers, {}) @patch('settings.settings.DISPATCHER_WAIT_TO_CANCEL', min_to_sec(55)) @patch('settings.settings.USER_WAIT_TO_CANCEL', min_to_sec(44)) def test_buffer_event(self): """Test to verify how the mechanics of the dispatcher buffering orders work""" # Constants initial_time = hour_to_sec(16) placement_time = time(16, 0, 0) time_delta = min_to_sec(10) # Verifies two test cases for how the dispatcher buffers orders # For each test, assert the correct number of orders are buffered # Test 1: schedules the submission of three orders and assert that only two are buffered env = Environment(initial_time=initial_time) dispatcher = Dispatcher( env=env, buffering_policy=RollingBufferingPolicy(), matching_policy=DummyMatchingPolicy() ) order_1 = Order(order_id=1, placement_time=placement_time) order_2 = Order(order_id=2, placement_time=placement_time) order_3 = Order(order_id=3, placement_time=placement_time) dispatcher.order_submitted_event(order_1, preparation_time=time(16, 0, 27), ready_time=time(16, 10, 0)) dispatcher.order_submitted_event(order_2, preparation_time=time(16, 0, 43), ready_time=time(16, 10, 0)) dispatcher.order_submitted_event(order_3, preparation_time=time(18, 0, 0), ready_time=time(18, 10, 0)) env.run(until=initial_time + time_delta) self.assertEqual(len(dispatcher.unassigned_orders), 2) # Test 2: schedules the submission of three orders and assert that all three orders are buffered env = Environment(initial_time=initial_time) dispatcher = Dispatcher( env=env, buffering_policy=RollingBufferingPolicy(), matching_policy=DummyMatchingPolicy() ) order_1 = Order(order_id=1, placement_time=placement_time) order_2 = Order(order_id=2, placement_time=placement_time) order_3 = Order(order_id=3, placement_time=placement_time) dispatcher.order_submitted_event(order_1, preparation_time=time(16, 0, 27), ready_time=time(16, 10, 0)) dispatcher.order_submitted_event(order_2, preparation_time=time(16, 0, 43), ready_time=time(16, 10, 0)) dispatcher.order_submitted_event(order_3, preparation_time=time(16, 4, 1), ready_time=time(16, 14, 0)) env.run(until=initial_time + time_delta) self.assertEqual(len(dispatcher.unassigned_orders), 3) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_courier_log_off(self): """Test to verify how the dispatcher handles a courier logging off""" # Constants random.seed(12) initial_time = hour_to_sec(12) time_delta = hour_to_sec(2) service_time = min_to_sec(4) # Tests 3 cases: when the courier is idle, busy or available. # For each test, assert that the courier ends up being logged off # Test 1: the courier is idle env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( env=env, dispatcher=dispatcher, courier_id=69, on_time=time(12, 0, 0), off_time=time(13, 0, 0) ) env.run(until=initial_time + time_delta) self.assertEqual(dispatcher.idle_couriers, {}) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.dropping_off_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) self.assertEqual(dispatcher.logged_off_couriers, {courier.courier_id: courier}) # Test 2: the courier is busy env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( env=env, dispatcher=dispatcher, courier_id=69, on_time=time(12, 0, 0), off_time=time(13, 0, 0) ) courier.state.interrupt() env.process(courier._dropping_off_state( orders={21: Order(drop_off_service_time=service_time)} )) env.run(until=initial_time + time_delta) self.assertEqual(dispatcher.idle_couriers, {}) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.dropping_off_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) self.assertEqual(dispatcher.logged_off_couriers, {courier.courier_id: courier}) # Test 3: the courier is available env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( env=env, dispatcher=dispatcher, courier_id=69, on_time=time(12, 0, 0), off_time=time(13, 0, 0) ) courier.state.interrupt() env.process(courier._picking_up_state( orders={ 21: Order(drop_off_service_time=service_time, ready_time=time(12, 20, 0)) } )) env.run(until=initial_time + time_delta) self.assertEqual(dispatcher.idle_couriers, {}) self.assertEqual(dispatcher.moving_couriers, {}) self.assertEqual(dispatcher.dropping_off_couriers, {}) self.assertEqual(dispatcher.picking_up_couriers, {}) self.assertEqual(dispatcher.logged_off_couriers, {courier.courier_id: courier}) def test_prepositioning_notification_accepted_event(self): """Test to verify the mechanics of a prepositioning notification being accepted by a courier""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a prepositioning notification, a courier and sends the accepted event instruction = Route( stops=[ Stop(position=0, type=StopType.PREPOSITION), Stop(position=1, type=StopType.PREPOSITION) ] ) courier = Courier(dispatcher=dispatcher, env=env, courier_id=666, on_time=on_time, off_time=off_time) notification = Notification(courier=courier, instruction=instruction, type=NotificationType.PREPOSITIONING) dispatcher.notification_accepted_event(notification=notification, courier=courier) env.run(until=initial_time + min_to_sec(10)) # Verify order and courier properties are modified and it is allocated correctly self.assertIsNotNone(courier.active_route) self.assertEqual(courier.active_route, instruction) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_prepositioning_notification_rejected_event(self): """Test to verify the mechanics of a prepositioning notification being rejected by a courier""" # Constants initial_time = hour_to_sec(14) on_time = time(14, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a prepositioning notification, a courier and sends the rejected event instruction = Route( stops=[ Stop(position=0, type=StopType.PREPOSITION), Stop(position=1, type=StopType.PREPOSITION) ] ) courier = Courier(dispatcher=dispatcher, env=env, courier_id=981, on_time=on_time, off_time=off_time) notification = Notification(courier=courier, instruction=instruction, type=NotificationType.PREPOSITIONING) dispatcher.notification_rejected_event(notification=notification, courier=courier) env.run(until=initial_time + min_to_sec(30)) # Verify order and courier properties are modified and it is allocated correctly self.assertIsNone(courier.active_route)
from objects.user import User from objects.location import Location u = User() u.username = "******" u.password = "******" u.email = "*****@*****.**" u.ip = "123.123.123.123" print u.create() l = Location() l.name = "City" l.id = "[1,2,3]" l.x = 1 l.y = 2 l.z = 3 l.description = "a small city near a river" print l.create()
class TestsCourier(unittest.TestCase): """Tests for the Courier actor class""" # Properties to be reused courier_id = 56 vehicle = Vehicle.MOTORCYCLE start_location = Location(lat=4.697893, lng=-74.051565) order_id = 0 pick_up_at = Location(lat=4.689697, lng=-74.055495) drop_off_at = Location(lat=4.690296, lng=-74.043929) placement_time = time(12, 0, 0) expected_drop_off_time = time(12, 40, 0) preparation_time = time(12, 2, 0) ready_time = time(12, 12, 0) # Services to be reused acceptance_policy = UniformAcceptancePolicy() movement_evaluation_policy = NeighborsMoveEvalPolicy() movement_policy = OSRMMovementPolicy() @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) def test_always_idle(self, osrm): """Test to evaluate a courier never moving""" # Constants random.seed(187) on_time = time(0, 0, 0) off_time = time(5, 0, 0) # Services env = Environment() dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier and runs a simulation courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, on_time=on_time, off_time=off_time) env.run(until=hour_to_sec(4)) # Asserts that the courier is idle and never moved self.assertEqual(courier.condition, 'idle') self.assertEqual(courier.location, self.start_location) self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.95) @patch('settings.settings.COURIER_WAIT_TO_MOVE', min_to_sec(7)) def test_movement_state(self, osrm): """Test to evaluate how a courier moves with dummy movement""" # Constants random.seed(365) on_time = time(0, 0, 0) off_time = time(5, 0, 0) env = Environment() dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier and runs a simulation courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, on_time=on_time, off_time=off_time) env.run(until=hour_to_sec(4) + min_to_sec(5)) # Asserts that the courier moved and is is in a different location self.assertEqual(courier.condition, 'moving') self.assertNotEqual(courier.location, self.start_location) self.assertIn(courier.courier_id, dispatcher.moving_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.1) @patch('settings.settings.COURIER_MIN_ACCEPTANCE_RATE', 0.99) def test_notify_event_accept_idle(self, osrm): """Test to evaluate how a courier handles a notification while being idle and accepts it""" # Constants random.seed(126) initial_time = hour_to_sec(12) time_delta = min_to_sec(40) on_time = time(12, 0, 0) off_time = time(13, 0, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with high acceptance rate and immediately send a new instruction, composed of a single order courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.99, on_time=on_time, off_time=off_time) order = Order(order_id=self.order_id, drop_off_at=self.drop_off_at, pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, user=User(env=env)) dispatcher.unassigned_orders[order.order_id] = order instruction = Route(orders={self.order_id: order}, stops=[ Stop(location=self.pick_up_at, position=0, orders={self.order_id: order}, type=StopType.PICK_UP, visited=False), Stop(location=self.drop_off_at, position=1, orders={self.order_id: order}, type=StopType.DROP_OFF, visited=False) ]) notification = Notification(courier=courier, instruction=instruction) env.process(courier.notification_event(notification)) env.run(until=initial_time + time_delta) # Asserts that the courier fulfilled the route and is at a different start location self.assertIsNotNone(order.pick_up_time) self.assertIsNotNone(order.drop_off_time) self.assertEqual(order.courier_id, courier.courier_id) self.assertTrue(order.pick_up_time < order.drop_off_time) self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertEqual(dispatcher.fulfilled_orders, {order.order_id: order}) self.assertEqual(order.state, 'dropped_off') self.assertNotEqual(courier.location, self.start_location) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.1) @patch('settings.settings.COURIER_MIN_ACCEPTANCE_RATE', 0.99) def test_notify_event_reject_idle(self, osrm): """Test to evaluate how a courier handles a notification while being idle and rejects it""" # Constants random.seed(122) on_time = time(12, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=hour_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with low acceptance rate and immediately send a new instruction, composed of a single order courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.01, on_time=on_time, off_time=off_time) order = Order(order_id=self.order_id, drop_off_at=self.drop_off_at, pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time) dispatcher.unassigned_orders[order.order_id] = order instruction = Route(orders={self.order_id: order}, stops=[ Stop(location=self.pick_up_at, position=0, orders={self.order_id: order}, type=StopType.PICK_UP, visited=False), Stop(location=self.drop_off_at, position=1, orders={self.order_id: order}, type=StopType.DROP_OFF, visited=False) ]) notification = Notification(courier=courier, instruction=instruction) env.process(courier.notification_event(notification)) env.run(until=hour_to_sec(14)) # Asserts that the courier didn't fulfill the route self.assertIsNone(order.pick_up_time) self.assertIsNone(order.drop_off_time) self.assertIsNone(order.courier_id) self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertIn(courier.courier_id, order.rejected_by) self.assertIn(order.order_id, courier.rejected_orders) self.assertEqual(dispatcher.unassigned_orders, {order.order_id: order}) self.assertEqual(order.state, 'unassigned') self.assertEqual(courier.location, self.start_location) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.99) @patch('settings.settings.COURIER_MIN_ACCEPTANCE_RATE', 0.99) def test_notify_event_accept_picking_up(self, osrm): """Test to evaluate how a courier handles a notification while picking up and accepts it""" # Constants random.seed(184) on_time = time(12, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=hour_to_sec(12) + min_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with high acceptance rate, an active route and in state of picking up. # Sends a new instruction, composed of a single new order active_order = Order( order_id=self.order_id, drop_off_at=self.drop_off_at, pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, courier_id=self.courier_id, user=User(env=env)) dispatcher.assigned_orders[active_order.order_id] = active_order new_order = Order(order_id=17, drop_off_at=Location(lat=4.694627, lng=-74.038886), pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, user=User(env=env)) dispatcher.unassigned_orders[new_order.order_id] = new_order courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=active_order.pick_up_at, acceptance_rate=0.99, active_route=Route(orders={self.order_id: active_order}, stops=[ Stop(location=self.pick_up_at, position=0, orders={self.order_id: active_order}, type=StopType.PICK_UP, visited=False), Stop(location=self.drop_off_at, position=1, orders={self.order_id: active_order}, type=StopType.DROP_OFF, visited=False) ]), on_time=on_time, off_time=off_time) instruction = [ Stop(location=new_order.drop_off_at, position=1, orders={new_order.order_id: new_order}, type=StopType.DROP_OFF, visited=False) ] notification = Notification(courier=courier, instruction=instruction) courier.state.interrupt() courier.active_stop = courier.active_route.stops[0] courier.state = env.process( courier._picking_up_state( orders={active_order.order_id: active_order})) env.process(courier.notification_event(notification)) env.run(until=hour_to_sec(13) + min_to_sec(12)) # Asserts that the courier fulfilled the active and new order and is at a different start location self.assertIsNotNone(active_order.pick_up_time) self.assertIsNotNone(active_order.drop_off_time) self.assertEqual(active_order.courier_id, courier.courier_id) self.assertTrue(active_order.pick_up_time < active_order.drop_off_time) self.assertEqual(active_order.state, 'dropped_off') self.assertIsNotNone(new_order.pick_up_time) self.assertIsNotNone(new_order.drop_off_time) self.assertEqual(new_order.courier_id, courier.courier_id) self.assertTrue(new_order.pick_up_time < new_order.drop_off_time) self.assertEqual(new_order.state, 'dropped_off') self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertNotEqual(courier.location, self.start_location) self.assertEqual(dispatcher.fulfilled_orders, { active_order.order_id: active_order, new_order.order_id: new_order }) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) @patch('settings.settings.COURIER_MIN_ACCEPTANCE_RATE', 0.99) def test_notify_event_reject_picking_up(self, osrm): """Test to evaluate how a courier handles a notification while picking up and rejects it""" # Constants random.seed(4747474) on_time = time(12, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=hour_to_sec(12) + min_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with low acceptance rate, an active route and in state of picking up. # Sends a new instruction, composed of a single new order active_order = Order( order_id=self.order_id, drop_off_at=self.drop_off_at, pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, courier_id=self.courier_id, user=User(env=env)) dispatcher.assigned_orders[active_order.order_id] = active_order new_order = Order(order_id=17, drop_off_at=Location(lat=4.694627, lng=-74.038886), pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, user=User(env=env)) dispatcher.unassigned_orders[new_order.order_id] = new_order courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=active_order.pick_up_at, acceptance_rate=0.01, active_route=Route(orders={self.order_id: active_order}, stops=[ Stop(location=self.pick_up_at, position=0, orders={self.order_id: active_order}, type=StopType.PICK_UP, visited=False), Stop(location=self.drop_off_at, position=1, orders={self.order_id: active_order}, type=StopType.DROP_OFF, visited=False) ]), on_time=on_time, off_time=off_time) instruction = Stop(location=new_order.drop_off_at, position=1, orders={new_order.order_id: new_order}, type=StopType.DROP_OFF, visited=False) notification = Notification(courier=courier, instruction=instruction) courier.state.interrupt() courier.active_stop = courier.active_route.stops[0] courier.state = env.process( courier._picking_up_state( orders={active_order.order_id: active_order})) env.process(courier.notification_event(notification)) env.run(until=hour_to_sec(14)) # Asserts: # - the courier didn't fulfill the new order, # - fulfilled the active order and # - is at a different start location. self.assertIsNone(new_order.pick_up_time) self.assertIsNone(new_order.drop_off_time) self.assertIsNone(new_order.courier_id) self.assertIn(courier.courier_id, new_order.rejected_by) self.assertIn(new_order.order_id, courier.rejected_orders) self.assertEqual(new_order.state, 'unassigned') self.assertIsNotNone(active_order.pick_up_time) self.assertIsNotNone(active_order.drop_off_time) self.assertEqual(active_order.courier_id, courier.courier_id) self.assertTrue(active_order.pick_up_time < active_order.drop_off_time) self.assertEqual(active_order.state, 'dropped_off') self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertNotEqual(courier.location, self.start_location) self.assertEqual(dispatcher.fulfilled_orders, {active_order.order_id: active_order}) self.assertEqual(dispatcher.unassigned_orders, {new_order.order_id: new_order}) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_log_off(self): """Test to evaluate the scheduling of the courier logging off works correctly""" # Constants random.seed(888) on_time = time(8, 0, 0) off_time = time(14, 0, 0) initial_time = hour_to_sec(8) # Verifies for two test cases the scheduling of the courier logging off works correctly. # In one case, the courier log off event doesn't yet happen. In the other test, it does # Test 1: the courier achieves the log off after a given time env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(env=env, dispatcher=dispatcher, courier_id=84, on_time=on_time, off_time=off_time) env.run(until=initial_time + hour_to_sec(10)) self.assertEqual(dispatcher.logged_off_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.idle_couriers, {}) self.assertEqual(courier.condition, 'logged_off') # Test 2: the courier doesn't achieve the log off because the simulation ends before env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier(env=env, dispatcher=dispatcher, courier_id=84, on_time=on_time, off_time=off_time) env.run(until=initial_time + hour_to_sec(2)) self.assertEqual(dispatcher.idle_couriers, {courier.courier_id: courier}) self.assertEqual(dispatcher.logged_off_couriers, {}) self.assertEqual(courier.condition, 'idle') @patch('settings.settings.COURIER_EARNINGS_PER_ORDER', 3) @patch('settings.settings.COURIER_EARNINGS_PER_HOUR', 8) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_calculate_earnings(self): """Test to verify the mechanics of calculating the shift's earnings""" # Constants random.seed(523) on_time = time(0, 0, 0) off_time = time(2, 0, 0) # Services env = Environment() # Creates a two hour - shift courier courier = Courier(env=env, on_time=on_time, off_time=off_time) # Verifies for two scenarios how the earnings are calculated. # In the first test, raw earnings from orders are chosen. # In the second test, the hourly earnings rate is chosen. # Test 1. Creates courier earnings to select the raw earnings from orders. # Asserts that these earnings are selected over the hourly earnings rate courier.fulfilled_orders = [Order()] * 7 courier.earnings = courier._calculate_earnings() self.assertEqual( courier.earnings, len(courier.fulfilled_orders) * settings.COURIER_EARNINGS_PER_ORDER) # Test 2. Creates courier earnings to select the hourly earnings rate. # Asserts that these earnings are selected over the order earnings courier.fulfilled_orders = [Order()] * 2 courier.earnings = courier._calculate_earnings() self.assertEqual( courier.earnings, sec_to_hour(time_diff(courier.off_time, courier.on_time)) * settings.COURIER_EARNINGS_PER_HOUR) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.1) def test_notify_prepositioning_event_accept_idle(self, osrm): """Test to evaluate how a courier handles a prepositioning notification while being idle and accepts it""" # Constants random.seed(348) initial_time = hour_to_sec(17) time_delta = min_to_sec(10) on_time = time(17, 0, 0) off_time = time(17, 30, 0) # Services env = Environment(initial_time=initial_time) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with high acceptance rate and immediately send a prepositioning notification courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.99, on_time=on_time, off_time=off_time) instruction = Route(orders=None, stops=[ Stop(location=self.pick_up_at, position=0, orders=None, type=StopType.PREPOSITION, visited=False) ]) notification = Notification(courier=courier, instruction=instruction, type=NotificationType.PREPOSITIONING) env.process(courier.notification_event(notification)) env.run(until=initial_time + time_delta) # Asserts that the courier fulfilled the route and is at a different start location self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertEqual(dispatcher.fulfilled_orders, {}) self.assertNotEqual(courier.location, self.start_location) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_notify_prepositioning_event_reject_idle(self, osrm): """Test to evaluate how a courier handles a prepositioning notification while being idle and rejects it""" # Constants random.seed(672) on_time = time(6, 0, 0) off_time = time(8, 0, 0) # Services env = Environment(initial_time=hour_to_sec(6)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with low acceptance rate and immediately send a prepositioning notification courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.01, on_time=on_time, off_time=off_time) instruction = Route(orders=None, stops=[ Stop(location=self.pick_up_at, position=0, orders=None, type=StopType.PREPOSITION, visited=False) ]) notification = Notification(courier=courier, instruction=instruction, type=NotificationType.PREPOSITIONING) env.process(courier.notification_event(notification)) env.run(until=hour_to_sec(7)) # Asserts that the courier didn't fulfill the route self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertEqual(courier.location, self.start_location) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys()) @patch('services.osrm_service.OSRMService.get_route', side_effect=mocked_get_route) @patch('settings.settings.COURIER_MOVEMENT_PROBABILITY', 0.01) def test_pick_up_waiting_time(self, osrm): """Test to verify the mechanics of the waiting time are correctly designed""" # Constants random.seed(290) on_time = time(6, 0, 0) off_time = time(8, 0, 0) # Services env = Environment(initial_time=hour_to_sec(6)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier and sets it to pick up stuff courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.01, on_time=on_time, off_time=off_time) order = Order(ready_time=time(6, 15, 0), order_id=23) stop = Stop(orders={order.order_id: order}, type=StopType.PICK_UP) env.process(courier._execute_stop(stop)) dispatcher.state.interrupt() # Run until there are no more events and assert the courier experienced waiting time. env.run(until=hour_to_sec(7)) self.assertTrue(order.pick_up_time >= time( 6, int(order.ready_time.minute + order.pick_up_service_time / 60))) # For another test, if the order's ready time has expired, the courier doesn't experience waiting time env = Environment(initial_time=hour_to_sec(6)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=self.start_location, acceptance_rate=0.01, on_time=on_time, off_time=off_time) order = Order(ready_time=time(4, 0, 0), order_id=23) stop = Stop(orders={order.order_id: order}, type=StopType.PICK_UP) env.process(courier._execute_stop(stop)) dispatcher.state.interrupt() env.run(until=hour_to_sec(7)) self.assertTrue( time(order.pick_up_time.hour, order.pick_up_time.minute) <= time( 6, int(order.pick_up_service_time / 60)))
def test_notify_event_reject_picking_up(self, osrm): """Test to evaluate how a courier handles a notification while picking up and rejects it""" # Constants random.seed(4747474) on_time = time(12, 0, 0) off_time = time(15, 0, 0) # Services env = Environment(initial_time=hour_to_sec(12) + min_to_sec(12)) dispatcher = Dispatcher(env=env, matching_policy=DummyMatchingPolicy()) # Creates a courier with low acceptance rate, an active route and in state of picking up. # Sends a new instruction, composed of a single new order active_order = Order( order_id=self.order_id, drop_off_at=self.drop_off_at, pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, courier_id=self.courier_id, user=User(env=env)) dispatcher.assigned_orders[active_order.order_id] = active_order new_order = Order(order_id=17, drop_off_at=Location(lat=4.694627, lng=-74.038886), pick_up_at=self.pick_up_at, placement_time=self.placement_time, expected_drop_off_time=self.expected_drop_off_time, preparation_time=self.preparation_time, ready_time=self.ready_time, user=User(env=env)) dispatcher.unassigned_orders[new_order.order_id] = new_order courier = Courier( acceptance_policy=self.acceptance_policy, dispatcher=dispatcher, env=env, movement_evaluation_policy=self.movement_evaluation_policy, movement_policy=self.movement_policy, courier_id=self.courier_id, vehicle=self.vehicle, location=active_order.pick_up_at, acceptance_rate=0.01, active_route=Route(orders={self.order_id: active_order}, stops=[ Stop(location=self.pick_up_at, position=0, orders={self.order_id: active_order}, type=StopType.PICK_UP, visited=False), Stop(location=self.drop_off_at, position=1, orders={self.order_id: active_order}, type=StopType.DROP_OFF, visited=False) ]), on_time=on_time, off_time=off_time) instruction = Stop(location=new_order.drop_off_at, position=1, orders={new_order.order_id: new_order}, type=StopType.DROP_OFF, visited=False) notification = Notification(courier=courier, instruction=instruction) courier.state.interrupt() courier.active_stop = courier.active_route.stops[0] courier.state = env.process( courier._picking_up_state( orders={active_order.order_id: active_order})) env.process(courier.notification_event(notification)) env.run(until=hour_to_sec(14)) # Asserts: # - the courier didn't fulfill the new order, # - fulfilled the active order and # - is at a different start location. self.assertIsNone(new_order.pick_up_time) self.assertIsNone(new_order.drop_off_time) self.assertIsNone(new_order.courier_id) self.assertIn(courier.courier_id, new_order.rejected_by) self.assertIn(new_order.order_id, courier.rejected_orders) self.assertEqual(new_order.state, 'unassigned') self.assertIsNotNone(active_order.pick_up_time) self.assertIsNotNone(active_order.drop_off_time) self.assertEqual(active_order.courier_id, courier.courier_id) self.assertTrue(active_order.pick_up_time < active_order.drop_off_time) self.assertEqual(active_order.state, 'dropped_off') self.assertIsNone(courier.active_route) self.assertIsNone(courier.active_stop) self.assertNotEqual(courier.location, self.start_location) self.assertEqual(dispatcher.fulfilled_orders, {active_order.order_id: active_order}) self.assertEqual(dispatcher.unassigned_orders, {new_order.order_id: new_order}) self.assertEqual(courier.condition, 'idle') self.assertIn(courier.courier_id, dispatcher.idle_couriers.keys())
def test_generate_matching_prospects_picking_up_couriers(self, osrm): """Test to verify how prospects are created""" # Constants env_time = hour_to_sec(12) + min_to_sec(20) on_time = time(8, 0, 0) off_time = time(16, 0, 0) # Orders order_1 = Order(order_id=1, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.681694, lng=-74.044811), ready_time=time(12, 30, 0), expected_drop_off_time=time(12, 40, 0), pick_up_service_time=0, drop_off_service_time=0) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.695001, lng=-74.040737), ready_time=time(12, 32, 0), expected_drop_off_time=time(12, 42, 0), pick_up_service_time=0, drop_off_service_time=0) order_3 = Order(order_id=3, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.668742, lng=-74.056684), ready_time=time(12, 33, 0), expected_drop_off_time=time(12, 43, 0), pick_up_service_time=0, drop_off_service_time=0) # Couriers courier_3 = Courier( courier_id=3, on_time=on_time, off_time=off_time, condition='picking_up', location=order_3.pick_up_at, active_route=Route(orders={order_3.order_id: order_3}, stops=[ Stop(location=order_3.pick_up_at, orders={order_3.order_id: order_3}, position=0, type=StopType.PICK_UP), Stop(location=order_3.drop_off_at, orders={order_3.order_id: order_3}, position=1, type=StopType.DROP_OFF) ]), active_stop=Stop(location=order_3.pick_up_at, orders={order_3.order_id: order_3}, position=0, type=StopType.PICK_UP)) # Get routes and assert expected behavior policy = MyopicMatchingPolicy(assignment_updates=True, prospects=True, notification_filtering=False, mip_matcher=False) routes = policy._generate_routes(orders=[order_1, order_2], couriers=[courier_3], env_time=env_time) # Generate prospects and assert expected behavior prospects = policy._generate_matching_prospects(routes=routes, couriers=[courier_3], env_time=env_time) self.assertFalse(prospects.tolist())
def test_generate_matching_prospects_all(self, osrm): """Test to verify how prospects are created""" # Constants env_time = hour_to_sec(12) + min_to_sec(20) on_time = time(8, 0, 0) off_time = time(16, 0, 0) # Orders order_1 = Order(order_id=1, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.681694, lng=-74.044811), ready_time=time(12, 30, 0), expected_drop_off_time=time(12, 40, 0), pick_up_service_time=0, drop_off_service_time=0) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.695001, lng=-74.040737), ready_time=time(12, 32, 0), expected_drop_off_time=time(12, 42, 0), pick_up_service_time=0, drop_off_service_time=0) order_3 = Order(order_id=3, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.668742, lng=-74.056684), ready_time=time(12, 33, 0), expected_drop_off_time=time(12, 43, 0), pick_up_service_time=0, drop_off_service_time=0) order_4 = Order(order_id=4, pick_up_at=Location(lat=4.678759, lng=-74.055729), drop_off_at=Location(lat=4.661441, lng=-74.056955), ready_time=time(12, 34, 0), expected_drop_off_time=time(12, 44, 0), pick_up_service_time=0, drop_off_service_time=0) # Couriers courier_1 = Courier(courier_id=1, on_time=on_time, off_time=off_time, condition='idle', location=Location(lat=4.676854, lng=-74.057498)) courier_2 = Courier(courier_id=2, on_time=on_time, off_time=off_time, condition='idle', location=Location(lat=4.679408, lng=-74.052524)) # Routes policy = MyopicMatchingPolicy(assignment_updates=True, prospects=True, notification_filtering=False, mip_matcher=False) routes = policy._generate_routes( orders=[order_1, order_2, order_3, order_4], couriers=[courier_1, courier_2], env_time=env_time) # Generate prospects and assert expected behavior prospects = policy._generate_matching_prospects( routes=routes, couriers=[courier_1, courier_2], env_time=env_time) self.assertTrue(prospects.tolist()) self.assertEqual(len(prospects), 8)
def test_add_order(self, osrm): """Test to verify how a new order is added to an existing route""" # Constants old_order = Order(order_id=5, pick_up_at=Location(lat=4.567, lng=1.234), drop_off_at=Location(lat=1.234, lng=4.567)) new_order = Order(order_id=1, pick_up_at=Location(lat=1.234, lng=4.567), drop_off_at=Location(lat=4.567, lng=1.234)) # Case 1: the route is empty route = Route(num_stops=2) route.add_order(new_order) self.assertTrue(route.stops) self.assertEqual(len(route.stops), 2) self.assertEqual(len(route.stops), route.num_stops) self.assertIn(new_order.order_id, route.orders.keys()) self.assertIn(new_order.order_id, route.stops[0].orders.keys()) self.assertIn(new_order.order_id, route.stops[1].orders.keys()) self.assertEqual(route.stops[0].type, StopType.PICK_UP) self.assertEqual(route.stops[1].type, StopType.DROP_OFF) self.assertTrue(route.time) # Case 2. the route has an order and is inserted at correct position route = Route(orders={old_order.order_id: old_order}, stops=[ Stop(location=old_order.pick_up_at, orders={old_order.order_id: old_order}, position=0, type=StopType.PICK_UP), Stop(location=old_order.drop_off_at, orders={old_order.order_id: old_order}, position=1, type=StopType.DROP_OFF) ]) route.add_order(new_order, route_position=2) self.assertTrue(route) self.assertEqual(len(route.stops), 3) self.assertEqual(len(route.stops), route.num_stops) self.assertIn(new_order.order_id, route.orders.keys()) self.assertIn(old_order.order_id, route.orders.keys()) self.assertIn(new_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[1].orders.keys()) self.assertIn(new_order.order_id, route.stops[2].orders.keys()) self.assertEqual(route.stops[0].type, StopType.PICK_UP) self.assertEqual(route.stops[1].type, StopType.DROP_OFF) self.assertEqual(route.stops[2].type, StopType.DROP_OFF) self.assertTrue(route.time) # Case 3. the route has an order and is inserted at wrong position (greater position) route = Route(orders={old_order.order_id: old_order}, stops=[ Stop(location=old_order.pick_up_at, orders={old_order.order_id: old_order}, position=0, type=StopType.PICK_UP), Stop(location=old_order.drop_off_at, orders={old_order.order_id: old_order}, position=1, type=StopType.DROP_OFF) ]) route.add_order(new_order, route_position=6) self.assertTrue(route) self.assertEqual(len(route.stops), 3) self.assertEqual(len(route.stops), route.num_stops) self.assertIn(new_order.order_id, route.orders.keys()) self.assertIn(old_order.order_id, route.orders.keys()) self.assertIn(new_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[1].orders.keys()) self.assertIn(new_order.order_id, route.stops[2].orders.keys()) self.assertEqual(route.stops[0].type, StopType.PICK_UP) self.assertEqual(route.stops[1].type, StopType.DROP_OFF) self.assertEqual(route.stops[2].type, StopType.DROP_OFF) self.assertTrue(route.time) # Case 4. the route has an order and is inserted at wrong position (equal position) route = Route(orders={old_order.order_id: old_order}, stops=[ Stop(location=old_order.pick_up_at, orders={old_order.order_id: old_order}, position=0, type=StopType.PICK_UP), Stop(location=old_order.drop_off_at, orders={old_order.order_id: old_order}, position=1, type=StopType.DROP_OFF) ]) route.add_order(new_order, route_position=1) self.assertTrue(route) self.assertEqual(len(route.stops), 3) self.assertEqual(len(route.stops), route.num_stops) self.assertIn(new_order.order_id, route.orders.keys()) self.assertIn(old_order.order_id, route.orders.keys()) self.assertIn(new_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[1].orders.keys()) self.assertIn(new_order.order_id, route.stops[2].orders.keys()) self.assertEqual(route.stops[0].type, StopType.PICK_UP) self.assertEqual(route.stops[1].type, StopType.DROP_OFF) self.assertEqual(route.stops[2].type, StopType.DROP_OFF) self.assertTrue(route.time) # Case 5. the route has an order and is inserted at wrong position (smaller position) route = Route(orders={old_order.order_id: old_order}, stops=[ Stop(location=old_order.pick_up_at, orders={old_order.order_id: old_order}, position=0, type=StopType.PICK_UP), Stop(location=old_order.drop_off_at, orders={old_order.order_id: old_order}, position=1, type=StopType.DROP_OFF) ]) route.add_order(new_order, route_position=1) self.assertTrue(route) self.assertEqual(len(route.stops), 3) self.assertEqual(len(route.stops), route.num_stops) self.assertIn(new_order.order_id, route.orders.keys()) self.assertIn(old_order.order_id, route.orders.keys()) self.assertIn(new_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[0].orders.keys()) self.assertIn(old_order.order_id, route.stops[1].orders.keys()) self.assertIn(new_order.order_id, route.stops[2].orders.keys()) self.assertEqual(route.stops[0].type, StopType.PICK_UP) self.assertEqual(route.stops[1].type, StopType.DROP_OFF) self.assertEqual(route.stops[2].type, StopType.DROP_OFF) self.assertTrue(route.time)
def test_generate_group_routes(self, osrm): """Test to verify how the heuristic to generate routes work""" # Constants order_1 = Order(order_id=1, pick_up_at=Location(lat=4.678417, lng=-74.054725), drop_off_at=Location(lat=4.717045, lng=-74.036359), ready_time=time(12, 13, 0)) order_2 = Order(order_id=2, pick_up_at=Location(lat=4.678417, lng=-74.054725), drop_off_at=Location(lat=4.723418, lng=-74.037067), ready_time=time(12, 10, 0)) order_3 = Order(order_id=3, pick_up_at=Location(lat=4.678417, lng=-74.054725), drop_off_at=Location(lat=4.723418, lng=-74.037067), ready_time=time(12, 30, 0)) old_order = Order(order_id=9898, pick_up_at=Location(lat=4.678417, lng=-74.054725), drop_off_at=Location(lat=4.727278, lng=-74.039299), ready_time=time(11, 50, 0)) target_size = 2 # Case 1: orders routed without initial routes and courier slack num_idle_couriers = 4 routes = MyopicMatchingPolicy._generate_group_routes( orders=[order_1, order_2], target_size=target_size, courier_routes=[], num_idle_couriers=num_idle_couriers) self.assertTrue(routes) self.assertEqual(len(routes), 2) routed_orders = [o for route in routes for o in route.orders.keys()] for order in [order_1, order_2]: self.assertIn(order.order_id, routed_orders) # Case 2: orders routed without initial routes and no courier slack num_idle_couriers = 0 routes = MyopicMatchingPolicy._generate_group_routes( orders=[order_1, order_2], target_size=target_size, courier_routes=[], num_idle_couriers=num_idle_couriers) self.assertTrue(routes) self.assertEqual(len(routes), 1) routed_orders = [o for route in routes for o in route.orders.keys()] for order in [order_1, order_2]: self.assertIn(order.order_id, routed_orders) # Case 3: orders routed with initial routes and courier slack num_idle_couriers = 1 initial_route = Route(orders={old_order.order_id: old_order}, stops=[ Stop(orders={old_order.order_id: old_order}, location=old_order.pick_up_at, position=0, type=StopType.PICK_UP), Stop(orders={old_order.order_id: old_order}, location=old_order.drop_off_at, position=1, type=StopType.DROP_OFF) ]) routes = MyopicMatchingPolicy._generate_group_routes( orders=[order_1, order_2], target_size=2, courier_routes=[initial_route], num_idle_couriers=num_idle_couriers, max_orders=3, courier_ids=[3]) self.assertTrue(routes) self.assertEqual(len(routes), 1) routed_orders = [o for route in routes for o in route.orders.keys()] for order in [order_1, order_2]: self.assertIn(order.order_id, routed_orders) self.assertIsNone(routes[0].initial_prospect) # Case 4: orders routed without initial routes and insufficient couriers num_idle_couriers = 0 target_size = 5 routes = MyopicMatchingPolicy._generate_group_routes( orders=[order_1, order_2, order_3], target_size=target_size, courier_routes=[], num_idle_couriers=num_idle_couriers) self.assertTrue(routes) self.assertEqual(len(routes), 1) routed_orders = [o for route in routes for o in route.orders.keys()] for order in [order_1, order_2, order_3]: self.assertIn(order.order_id, routed_orders)
def randomize_endpoint(): """Select random Yik Yak server to make requests from""" # Choose random location and locationize locationize_endpoint(Location(randint(-90, 90), randint(-180, 180)))
from objects.user import User from objects.location import Location u = User() u.username = "******" print u.fetch() l = Location() l.id = "[1,2,3]" print l.fetch()