class TravelRequestsSimulator(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection(host=mongodb_host, port=mongodb_port) log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='mongodb_database_connection: established') def clear_travel_requests_collection(self): """ Clear all the documents of the TravelRequests collection. :return: None """ self.mongodb_database_connection.clear_travel_request_documents_collection() log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='clear_travel_request_documents_collection: ok') def delete_travel_request_documents(self, object_ids=None, client_ids=None, bus_line_ids=None, min_departure_datetime=None, max_departure_datetime=None): """ Delete multiple travel_request_documents. :param object_ids: [ObjectId] :param client_ids: [int] :param bus_line_ids: [int] :param min_departure_datetime: datetime :param max_departure_datetime :return: None """ self.mongodb_database_connection.delete_travel_request_documents( object_ids=object_ids, client_ids=client_ids, bus_line_ids=bus_line_ids, min_departure_datetime=min_departure_datetime, max_departure_datetime=max_departure_datetime ) log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='delete_travel_request_documents: ok') def generate_random_travel_request_documents(self, initial_datetime, min_number_of_travel_request_documents, max_number_of_travel_request_documents): """ Generate random number of travel_request_documents for each bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. :param initial_datetime: datetime :param min_number_of_travel_request_documents: int :param max_number_of_travel_request_documents: int :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: number_of_travel_request_documents = random.randint( min_number_of_travel_request_documents, max_number_of_travel_request_documents ) self.generate_travel_request_documents( initial_datetime=initial_datetime, number_of_travel_request_documents=number_of_travel_request_documents, bus_line=bus_line ) def generate_travel_request_documents(self, initial_datetime, number_of_travel_request_documents, bus_line=None, bus_line_id=None): """ Generate a specific number of travel_request_documents, for the selected bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. :param initial_datetime: datetime :param number_of_travel_request_documents: int :param bus_line: bus_line_document :param bus_line_id: int :return: None """ # 1: The inputs: initial_datetime, number_of_travel_request_documents, and (bus_line or bus_line_id) # are provided to the Travel Requests Simulator, so as a specific number of travel_request_documents # to be generated, for the selected bus_line, for a 24hour period starting from # the selected datetime. # # 2: If the provided bus_line is None, then the Travel Requests Simulator retrieves from the System Database # the bus_line which corresponds to the provided bus_line_id. # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(bus_line_id=bus_line_id) else: pass bus_stops = bus_line.get('bus_stops') number_of_bus_stops = len(bus_stops) # 3: The Travel Requests Simulator generates the travel_request_documents, taking into consideration # the variation of transportation demand during the hours of the day. # # distribution_weighted_datetimes = [ # (initial_datetime + timedelta(hours=0), 1), # (initial_datetime + timedelta(hours=1), 1), # (initial_datetime + timedelta(hours=2), 1), # (initial_datetime + timedelta(hours=3), 1), # (initial_datetime + timedelta(hours=4), 1), # (initial_datetime + timedelta(hours=5), 1), # (initial_datetime + timedelta(hours=6), 1), # (initial_datetime + timedelta(hours=7), 1), # (initial_datetime + timedelta(hours=8), 1), # (initial_datetime + timedelta(hours=9), 1), # (initial_datetime + timedelta(hours=10), 1), # (initial_datetime + timedelta(hours=11), 1), # (initial_datetime + timedelta(hours=12), 1), # (initial_datetime + timedelta(hours=13), 1), # (initial_datetime + timedelta(hours=14), 1), # (initial_datetime + timedelta(hours=15), 1), # (initial_datetime + timedelta(hours=16), 1), # (initial_datetime + timedelta(hours=17), 1), # (initial_datetime + timedelta(hours=18), 1), # (initial_datetime + timedelta(hours=19), 1), # (initial_datetime + timedelta(hours=20), 1), # (initial_datetime + timedelta(hours=21), 1), # (initial_datetime + timedelta(hours=22), 1), # (initial_datetime + timedelta(hours=23), 1) # ] distribution_weighted_datetimes = [ (initial_datetime + timedelta(hours=i), travel_requests_simulator_datetime_distribution_weights[i]) for i in range(0, 24) ] datetime_population = [val for val, cnt in distribution_weighted_datetimes for i in range(cnt)] travel_request_documents = [] maximum_client_id = self.mongodb_database_connection.get_maximum_or_minimum(collection='travel_request') for i in range(0, number_of_travel_request_documents): client_id = maximum_client_id + 1 maximum_client_id = client_id starting_bus_stop_index = random.randint(0, number_of_bus_stops - 2) starting_bus_stop = bus_stops[starting_bus_stop_index] ending_bus_stop_index = random.randint(starting_bus_stop_index + 1, number_of_bus_stops - 1) ending_bus_stop = bus_stops[ending_bus_stop_index] additional_departure_time_interval = random.randint(0, 59) departure_datetime = (random.choice(datetime_population) + timedelta(minutes=additional_departure_time_interval)) travel_request_document = { 'client_id': client_id, 'bus_line_id': bus_line_id, 'starting_bus_stop': starting_bus_stop, 'ending_bus_stop': ending_bus_stop, 'departure_datetime': departure_datetime, 'arrival_datetime': None, 'starting_timetable_entry_index': None, 'ending_timetable_entry_index': None } travel_request_documents.append(travel_request_document) # 4: The generated travel_request_documents are stored at the # TravelRequests collection of the System Database. # self.mongodb_database_connection.insert_travel_request_documents( travel_request_documents=travel_request_documents )
class LookAheadHandler(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection(host=mongodb_host, port=mongodb_port) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='mongodb_database_connection: established') def generate_bus_line(self, bus_stop_names, bus_line_id=None): """ Generate a bus_line, consisted of a bus_line_id and a list of bus_stops, and store it to the corresponding collection of the System Database. Moreover, identify all the possible waypoints between the bus_stops of the bus_line, and populate the BusStopWaypoints collection. :param bus_stop_names: [string] :param bus_line_id: int :return: None """ # 1: The inputs: bus_line_id and bus_stop_names are provided to the function, so as as a bus_line # with the corresponding bus_line_id and bus_stops to be generated. # # 2: The Look Ahead connects to the System Database and retrieves the bus_stops which correspond to # the provided bus_stop_names. The function returns None and the bus_line is not generated, # in case there is a bus_stop_name which does not correspond to a stored bus_stop. # if bus_line_id is None: maximum_bus_line_id = self.mongodb_database_connection.get_maximum_or_minimum(collection='bus_line') bus_line_id = maximum_bus_line_id + 1 bus_stops = [] for bus_stop_name in bus_stop_names: bus_stop_document = self.mongodb_database_connection.find_bus_stop_document(name=bus_stop_name) if bus_stop_document is None: log_message = 'find_bus_stop_document (mongodb_database) - name:', bus_stop_name, '- result: None' log(module_name='look_ahead_handler', log_type='DEBUG', log_message=log_message) return None else: bus_stops.append(bus_stop_document) # 3: The intermediate waypoints of the bus_routes, which are generated while combining starting and # ending bus_stops of the bus_line, should be stored as bus_stop_waypoints_documents at the System Database. # The Look Ahead checks the existing bus_stop_waypoints_documents and communicates with the Route Generator # in order to identify the waypoints of the bus_routes which are not already stored. The newly generated # bus_stop_waypoints_documents are getting stored to the corresponding collection of the System Database. # The function returns None and the bus_line is not generated, in case the Route Generator can not identify # a possible route in order to connect the bus_stops of the bus_line. # number_of_bus_stops = len(bus_stops) for i in range(0, number_of_bus_stops - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] bus_stop_waypoints_document = self.mongodb_database_connection.find_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop ) if bus_stop_waypoints_document is None: route_generator_response = get_waypoints_between_two_bus_stops( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop ) if route_generator_response is None: log(module_name='look_ahead_handler', log_type='DEBUG', log_message='get_waypoints_between_two_bus_stops (route_generator): None') return None else: waypoints = route_generator_response.get('waypoints') if len(waypoints) == 0: log(module_name='look_ahead_handler', log_type='DEBUG', log_message='get_waypoints_between_two_bus_stops (route_generator): None') return None lists_of_edge_object_ids = [] for list_of_edges in waypoints: list_of_edge_object_ids = [] for edge in list_of_edges: edge_object_id = edge.get('_id') list_of_edge_object_ids.append(edge_object_id) lists_of_edge_object_ids.append(list_of_edge_object_ids) # waypoints: [[edge_object_id]] # waypoints = lists_of_edge_object_ids self.mongodb_database_connection.insert_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop, waypoints=waypoints ) # 4: The Look Ahead stores the newly generated bus_line_document, which is consisted of the bus_line_id # and the list of bus_stops, to the corresponding collection of the System Database. # In case there is an already existing bus_line_document, with the same bus_line_id, # then the list of bus_stops gets updated. # bus_line_document = {'bus_line_id': bus_line_id, 'bus_stops': bus_stops} self.mongodb_database_connection.insert_bus_line_document(bus_line_document=bus_line_document) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_bus_line_document (mongodb_database): ok') def generate_timetables_for_bus_line(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime, bus_line=None, bus_line_id=None): """ Generate timetables for a bus_line, for a selected datetime period, evaluating travel_requests of a specific datetime period. - The input: timetables_starting_datetime and input: timetables_ending_datetime are provided to the function, so as timetables for the specific datetime period to be generated. - The input: requests_min_departure_datetime and input: requests_max_departure_datetime are provided to the function, so as travel_requests with departure_datetime corresponding to the the specific datetime period to be evaluated. - The input: bus_line or input: bus_line_id is provided to the function, so as timetables for the specific bus_line to be generated. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :param bus_line: bus_line_document :param bus_line_id: int :return: None """ maximum_timetable_id_in_database = self.mongodb_database_connection.get_maximum_or_minimum( collection='timetable' ) # 1: The list of bus_stops corresponding to the provided bus_line is retrieved. # # bus_stop_document: { # '_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'} # } # bus_stops: [bus_stop_document] # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') # 2: The Look Ahead retrieves from the System Database the travel_requests with # departure_datetime higher than requests_min_departure_datetime and # lower than requests_max_departure_datetime. # # travel_request_document: { # '_id', 'client_id', 'bus_line_id', # 'starting_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'ending_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'departure_datetime', 'arrival_datetime' # } # travel_requests: [travel_request_document] # travel_requests = self.mongodb_database_connection.find_travel_request_documents( bus_line_ids=[bus_line_id], min_departure_datetime=requests_min_departure_datetime, max_departure_datetime=requests_max_departure_datetime ) # 3: (TimetableGenerator is initialized) The Look Ahead sends a request to the Route Generator so as # to identify the less time-consuming bus_route between the bus_stops of bus_line, # while taking into consideration the current levels of traffic density. # timetable_generator = TimetableGenerator( maximum_timetable_id_in_database=maximum_timetable_id_in_database, bus_line_id=bus_line_id, bus_stops=bus_stops, travel_requests=travel_requests ) # The list of bus_stops of a bus_line might contain the same bus_stop_osm_ids more than once. # For this reason, each travel_request needs to be related with the correct index in the bus_stops list. # So, the values 'starting_timetable_entry_index' and 'ending_timetable_entry_index' are estimated. # correspond_travel_requests_to_bus_stops( travel_requests=timetable_generator.travel_requests, bus_stops=timetable_generator.bus_stops ) # 4: Based on the response of the Route Generator, which includes details about the followed bus_route, # and using only one bus vehicle, the Look Ahead generates some initial timetables which cover the # whole datetime period from timetables_starting_datetime to timetables_ending_datetime. # Initially, the list of travel requests of these timetables is empty, and the departure_datetime and # arrival_datetime values of the timetable_entries are based exclusively on the details of the bus_route. # In the next steps of the algorithm, these timetables are used in the initial clustering # of the travel requests. # timetable_generator.timetables = generate_initial_timetables( bus_line_id=bus_line_id, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, route_generator_response=timetable_generator.route_generator_response ) current_average_waiting_time_of_timetables = float('Inf') while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_generator.timetables, travel_requests=timetable_generator.travel_requests ) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables ) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_generator.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_generator.timetables) else: break print_timetables(timetables=timetable_generator.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id') ) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_generator.timetables ) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_timetable_documents (mongodb_database): ok') def generate_timetables_for_bus_lines(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime): """ Generate timetables for all bus_lines, for a selected datetime period, evaluating travel_requests of a specific datetime period. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.generate_timetables_for_bus_line( bus_line=bus_line, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, requests_min_departure_datetime=requests_min_departure_datetime, requests_max_departure_datetime=requests_max_departure_datetime ) def update_timetables_of_bus_line(self, bus_line=None, bus_line_id=None): """ Update the timetables of a bus_line, taking into consideration the current levels of traffic_density. :param bus_line: bus_line_document :param bus_line_id: int :return: None """ if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') timetables = self.mongodb_database_connection.find_timetable_documents(bus_line_ids=[bus_line_id]) travel_requests = get_travel_requests_of_timetables(timetables=timetables) timetable_updater = TimetableUpdater( bus_stops=bus_stops, timetables=timetables, travel_requests=travel_requests ) update_entries_of_timetables( timetables=timetable_updater.timetables, route_generator_response=timetable_updater.route_generator_response ) current_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=timetable_updater.timetables ) print_timetables(timetables=timetable_updater.timetables) while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_updater.timetables, travel_requests=timetable_updater.travel_requests ) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables ) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_updater.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_updater.timetables) else: break print_timetables(timetables=timetable_updater.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id') ) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_updater.timetables ) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='update_timetable_documents (mongodb_database): ok') def update_timetables_of_bus_lines(self): """ Update the timetables of all bus_lines, taking into consideration the current levels of traffic_density. :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.update_timetables_of_bus_line(bus_line=bus_line)
class LookAheadHandler(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection( host=mongodb_host, port=mongodb_port) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='mongodb_database_connection: established') def generate_bus_line(self, bus_stop_names, bus_line_id=None): """ Generate a bus_line, consisted of a bus_line_id and a list of bus_stops, and store it to the corresponding collection of the System Database. Moreover, identify all the possible waypoints between the bus_stops of the bus_line, and populate the BusStopWaypoints collection. :param bus_stop_names: [string] :param bus_line_id: int :return: None """ # 1: The inputs: bus_line_id and bus_stop_names are provided to the function, so as as a bus_line # with the corresponding bus_line_id and bus_stops to be generated. # # 2: The Look Ahead connects to the System Database and retrieves the bus_stops which correspond to # the provided bus_stop_names. The function returns None and the bus_line is not generated, # in case there is a bus_stop_name which does not correspond to a stored bus_stop. # if bus_line_id is None: maximum_bus_line_id = self.mongodb_database_connection.get_maximum_or_minimum( collection='bus_line') bus_line_id = maximum_bus_line_id + 1 bus_stops = [] for bus_stop_name in bus_stop_names: bus_stop_document = self.mongodb_database_connection.find_bus_stop_document( name=bus_stop_name) if bus_stop_document is None: log_message = 'find_bus_stop_document (mongodb_database) - name:', bus_stop_name, '- result: None' log(module_name='look_ahead_handler', log_type='DEBUG', log_message=log_message) return None else: bus_stops.append(bus_stop_document) # 3: The intermediate waypoints of the bus_routes, which are generated while combining starting and # ending bus_stops of the bus_line, should be stored as bus_stop_waypoints_documents at the System Database. # The Look Ahead checks the existing bus_stop_waypoints_documents and communicates with the Route Generator # in order to identify the waypoints of the bus_routes which are not already stored. The newly generated # bus_stop_waypoints_documents are getting stored to the corresponding collection of the System Database. # The function returns None and the bus_line is not generated, in case the Route Generator can not identify # a possible route in order to connect the bus_stops of the bus_line. # number_of_bus_stops = len(bus_stops) for i in range(0, number_of_bus_stops - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] bus_stop_waypoints_document = self.mongodb_database_connection.find_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop) if bus_stop_waypoints_document is None: route_generator_response = get_waypoints_between_two_bus_stops( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop) if route_generator_response is None: log(module_name='look_ahead_handler', log_type='DEBUG', log_message= 'get_waypoints_between_two_bus_stops (route_generator): None' ) return None else: waypoints = route_generator_response.get('waypoints') if len(waypoints) == 0: log(module_name='look_ahead_handler', log_type='DEBUG', log_message= 'get_waypoints_between_two_bus_stops (route_generator): None' ) return None lists_of_edge_object_ids = [] for list_of_edges in waypoints: list_of_edge_object_ids = [] for edge in list_of_edges: edge_object_id = edge.get('_id') list_of_edge_object_ids.append(edge_object_id) lists_of_edge_object_ids.append( list_of_edge_object_ids) # waypoints: [[edge_object_id]] # waypoints = lists_of_edge_object_ids self.mongodb_database_connection.insert_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop, waypoints=waypoints) # 4: The Look Ahead stores the newly generated bus_line_document, which is consisted of the bus_line_id # and the list of bus_stops, to the corresponding collection of the System Database. # In case there is an already existing bus_line_document, with the same bus_line_id, # then the list of bus_stops gets updated. # bus_line_document = { 'bus_line_id': bus_line_id, 'bus_stops': bus_stops } self.mongodb_database_connection.insert_bus_line_document( bus_line_document=bus_line_document) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_bus_line_document (mongodb_database): ok') def generate_timetables_for_bus_line(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime, bus_line=None, bus_line_id=None): """ Generate timetables for a bus_line, for a selected datetime period, evaluating travel_requests of a specific datetime period. - The input: timetables_starting_datetime and input: timetables_ending_datetime are provided to the function, so as timetables for the specific datetime period to be generated. - The input: requests_min_departure_datetime and input: requests_max_departure_datetime are provided to the function, so as travel_requests with departure_datetime corresponding to the the specific datetime period to be evaluated. - The input: bus_line or input: bus_line_id is provided to the function, so as timetables for the specific bus_line to be generated. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :param bus_line: bus_line_document :param bus_line_id: int :return: None """ maximum_timetable_id_in_database = self.mongodb_database_connection.get_maximum_or_minimum( collection='timetable') # 1: The list of bus_stops corresponding to the provided bus_line is retrieved. # # bus_stop_document: { # '_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'} # } # bus_stops: [bus_stop_document] # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document( bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') # 2: The Look Ahead retrieves from the System Database the travel_requests with # departure_datetime higher than requests_min_departure_datetime and # lower than requests_max_departure_datetime. # # travel_request_document: { # '_id', 'client_id', 'bus_line_id', # 'starting_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'ending_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'departure_datetime', 'arrival_datetime' # } # travel_requests: [travel_request_document] # travel_requests = self.mongodb_database_connection.find_travel_request_documents( bus_line_ids=[bus_line_id], min_departure_datetime=requests_min_departure_datetime, max_departure_datetime=requests_max_departure_datetime) # 3: (TimetableGenerator is initialized) The Look Ahead sends a request to the Route Generator so as # to identify the less time-consuming bus_route between the bus_stops of bus_line, # while taking into consideration the current levels of traffic density. # timetable_generator = TimetableGenerator( maximum_timetable_id_in_database=maximum_timetable_id_in_database, bus_line_id=bus_line_id, bus_stops=bus_stops, travel_requests=travel_requests) # The list of bus_stops of a bus_line might contain the same bus_stop_osm_ids more than once. # For this reason, each travel_request needs to be related with the correct index in the bus_stops list. # So, the values 'starting_timetable_entry_index' and 'ending_timetable_entry_index' are estimated. # correspond_travel_requests_to_bus_stops( travel_requests=timetable_generator.travel_requests, bus_stops=timetable_generator.bus_stops) # 4: Based on the response of the Route Generator, which includes details about the followed bus_route, # and using only one bus vehicle, the Look Ahead generates some initial timetables which cover the # whole datetime period from timetables_starting_datetime to timetables_ending_datetime. # Initially, the list of travel requests of these timetables is empty, and the departure_datetime and # arrival_datetime values of the timetable_entries are based exclusively on the details of the bus_route. # In the next steps of the algorithm, these timetables are used in the initial clustering # of the travel requests. # timetable_generator.timetables = generate_initial_timetables( bus_line_id=bus_line_id, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, route_generator_response=timetable_generator. route_generator_response) current_average_waiting_time_of_timetables = float('Inf') while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_generator.timetables, travel_requests=timetable_generator.travel_requests) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_generator.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_generator.timetables) else: break print_timetables(timetables=timetable_generator.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id')) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_generator.timetables) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_timetable_documents (mongodb_database): ok') def generate_timetables_for_bus_lines(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime): """ Generate timetables for all bus_lines, for a selected datetime period, evaluating travel_requests of a specific datetime period. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.generate_timetables_for_bus_line( bus_line=bus_line, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, requests_min_departure_datetime=requests_min_departure_datetime, requests_max_departure_datetime=requests_max_departure_datetime ) def update_timetables_of_bus_line(self, bus_line=None, bus_line_id=None): """ Update the timetables of a bus_line, taking into consideration the current levels of traffic_density. :param bus_line: bus_line_document :param bus_line_id: int :return: None """ if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document( bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') timetables = self.mongodb_database_connection.find_timetable_documents( bus_line_ids=[bus_line_id]) travel_requests = get_travel_requests_of_timetables( timetables=timetables) timetable_updater = TimetableUpdater(bus_stops=bus_stops, timetables=timetables, travel_requests=travel_requests) update_entries_of_timetables( timetables=timetable_updater.timetables, route_generator_response=timetable_updater.route_generator_response ) current_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=timetable_updater.timetables) print_timetables(timetables=timetable_updater.timetables) while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_updater.timetables, travel_requests=timetable_updater.travel_requests) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_updater.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_updater.timetables) else: break print_timetables(timetables=timetable_updater.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id')) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_updater.timetables) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='update_timetable_documents (mongodb_database): ok') def update_timetables_of_bus_lines(self): """ Update the timetables of all bus_lines, taking into consideration the current levels of traffic_density. :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.update_timetables_of_bus_line(bus_line=bus_line)
class TravelRequestsSimulator(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection(host=mongodb_host, port=mongodb_port) log( module_name="travel_requests_simulator", log_type="DEBUG", log_message="mongodb_database_connection: established", ) def clear_travel_requests_collection(self): """ Clear all the documents of the TravelRequests collection. :return: None """ self.mongodb_database_connection.clear_travel_request_documents_collection() log( module_name="travel_requests_simulator", log_type="DEBUG", log_message="clear_travel_request_documents_collection: ok", ) def delete_travel_request_documents( self, object_ids=None, client_ids=None, line_ids=None, min_departure_datetime=None, max_departure_datetime=None ): """ Delete multiple travel_request_documents. travel_request_document: { '_id', 'client_id', 'line_id', 'starting_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, 'ending_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, 'departure_datetime', 'arrival_datetime', 'starting_timetable_entry_index', 'ending_timetable_entry_index' } :param object_ids: [ObjectId] :param client_ids: [int] :param line_ids: [int] :param min_departure_datetime: datetime :param max_departure_datetime :return: None """ self.mongodb_database_connection.delete_travel_request_documents( object_ids=object_ids, client_ids=client_ids, line_ids=line_ids, min_departure_datetime=min_departure_datetime, max_departure_datetime=max_departure_datetime, ) log( module_name="travel_requests_simulator", log_type="DEBUG", log_message="delete_travel_request_documents: ok" ) def generate_random_travel_request_documents( self, initial_datetime, min_number_of_travel_request_documents, max_number_of_travel_request_documents ): """ Generate random number of travel_request_documents for each bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. bus_line_document: { '_id', 'line_id', 'bus_stops': [{'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}] } :param initial_datetime: datetime :param min_number_of_travel_request_documents: int :param max_number_of_travel_request_documents: int :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: number_of_travel_request_documents = random.randint( min_number_of_travel_request_documents, max_number_of_travel_request_documents ) self.generate_travel_request_documents( initial_datetime=initial_datetime, number_of_travel_request_documents=number_of_travel_request_documents, bus_line=bus_line, ) def generate_travel_request_documents( self, initial_datetime, number_of_travel_request_documents, bus_line=None, line_id=None ): """ Generate a specific number of travel_request_documents, for the selected bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. bus_line_document: { '_id', 'line_id', 'bus_stops': [{'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}] } :param initial_datetime: datetime :param number_of_travel_request_documents: int :param bus_line: bus_line_document :param line_id: int :return: None """ # 1: The inputs: initial_datetime, number_of_travel_request_documents, and (bus_line or line_id) # are provided to the Travel Requests Simulator, so as a specific number of travel_request_documents # to be generated, for the selected bus_line, for a 24hour period starting from # the selected datetime. # # 2: If the provided bus_line is None, then the Travel Requests Simulator retrieves from the System Database # the bus_line which corresponds to the provided line_id. # if bus_line is None and line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(line_id=line_id) else: pass bus_stops = bus_line.get("bus_stops") number_of_bus_stops = len(bus_stops) # 3: The Travel Requests Simulator generates the travel_request_documents, taking into consideration # the variation of transportation demand during the hours of the day. # weighted_datetimes = [ (initial_datetime + timedelta(hours=0), 1), (initial_datetime + timedelta(hours=1), 1), (initial_datetime + timedelta(hours=2), 1), (initial_datetime + timedelta(hours=3), 1), (initial_datetime + timedelta(hours=4), 1), (initial_datetime + timedelta(hours=5), 1), (initial_datetime + timedelta(hours=6), 1), (initial_datetime + timedelta(hours=7), 1), (initial_datetime + timedelta(hours=8), 1), (initial_datetime + timedelta(hours=9), 1), (initial_datetime + timedelta(hours=10), 1), (initial_datetime + timedelta(hours=11), 1), (initial_datetime + timedelta(hours=12), 1), (initial_datetime + timedelta(hours=13), 1), (initial_datetime + timedelta(hours=14), 1), (initial_datetime + timedelta(hours=15), 1), (initial_datetime + timedelta(hours=16), 1), (initial_datetime + timedelta(hours=17), 1), (initial_datetime + timedelta(hours=18), 1), (initial_datetime + timedelta(hours=19), 1), (initial_datetime + timedelta(hours=20), 1), (initial_datetime + timedelta(hours=21), 1), (initial_datetime + timedelta(hours=22), 1), (initial_datetime + timedelta(hours=23), 1), ] datetime_population = [val for val, cnt in weighted_datetimes for i in range(cnt)] travel_request_documents = [] for i in range(0, number_of_travel_request_documents): client_id = i starting_bus_stop_index = random.randint(0, number_of_bus_stops - 2) starting_bus_stop = bus_stops[starting_bus_stop_index] ending_bus_stop_index = random.randint(starting_bus_stop_index + 1, number_of_bus_stops - 1) ending_bus_stop = bus_stops[ending_bus_stop_index] additional_departure_time_interval = random.randint(0, 59) departure_datetime = random.choice(datetime_population) + timedelta( minutes=additional_departure_time_interval ) travel_request_document = { "client_id": client_id, "line_id": line_id, "starting_bus_stop": starting_bus_stop, "ending_bus_stop": ending_bus_stop, "departure_datetime": departure_datetime, "arrival_datetime": None, "starting_timetable_entry_index": None, "ending_timetable_entry_index": None, } travel_request_documents.append(travel_request_document) # 4: The generated travel_request_documents are stored at the # TravelRequests collection of the System Database. # self.mongodb_database_connection.insert_travel_request_documents( travel_request_documents=travel_request_documents )
class TravelRequestsSimulator(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection( host=mongodb_host, port=mongodb_port) log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='mongodb_database_connection: established') def clear_travel_requests_collection(self): """ Clear all the documents of the TravelRequests collection. :return: None """ self.mongodb_database_connection.clear_travel_request_documents_collection( ) log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='clear_travel_request_documents_collection: ok') def delete_travel_request_documents(self, object_ids=None, client_ids=None, bus_line_ids=None, min_departure_datetime=None, max_departure_datetime=None): """ Delete multiple travel_request_documents. :param object_ids: [ObjectId] :param client_ids: [int] :param bus_line_ids: [int] :param min_departure_datetime: datetime :param max_departure_datetime :return: None """ self.mongodb_database_connection.delete_travel_request_documents( object_ids=object_ids, client_ids=client_ids, bus_line_ids=bus_line_ids, min_departure_datetime=min_departure_datetime, max_departure_datetime=max_departure_datetime) log(module_name='travel_requests_simulator', log_type='DEBUG', log_message='delete_travel_request_documents: ok') def generate_random_travel_request_documents( self, initial_datetime, min_number_of_travel_request_documents, max_number_of_travel_request_documents): """ Generate random number of travel_request_documents for each bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. :param initial_datetime: datetime :param min_number_of_travel_request_documents: int :param max_number_of_travel_request_documents: int :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: number_of_travel_request_documents = random.randint( min_number_of_travel_request_documents, max_number_of_travel_request_documents) self.generate_travel_request_documents( initial_datetime=initial_datetime, number_of_travel_request_documents= number_of_travel_request_documents, bus_line=bus_line) def generate_travel_request_documents(self, initial_datetime, number_of_travel_request_documents, bus_line=None, bus_line_id=None): """ Generate a specific number of travel_request_documents, for the selected bus_line, for a 24hour period starting from a selected datetime, and store them at the corresponding collection of the System Database. :param initial_datetime: datetime :param number_of_travel_request_documents: int :param bus_line: bus_line_document :param bus_line_id: int :return: None """ # 1: The inputs: initial_datetime, number_of_travel_request_documents, and (bus_line or bus_line_id) # are provided to the Travel Requests Simulator, so as a specific number of travel_request_documents # to be generated, for the selected bus_line, for a 24hour period starting from # the selected datetime. # # 2: If the provided bus_line is None, then the Travel Requests Simulator retrieves from the System Database # the bus_line which corresponds to the provided bus_line_id. # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document( bus_line_id=bus_line_id) else: pass bus_stops = bus_line.get('bus_stops') number_of_bus_stops = len(bus_stops) # 3: The Travel Requests Simulator generates the travel_request_documents, taking into consideration # the variation of transportation demand during the hours of the day. # # distribution_weighted_datetimes = [ # (initial_datetime + timedelta(hours=0), 1), # (initial_datetime + timedelta(hours=1), 1), # (initial_datetime + timedelta(hours=2), 1), # (initial_datetime + timedelta(hours=3), 1), # (initial_datetime + timedelta(hours=4), 1), # (initial_datetime + timedelta(hours=5), 1), # (initial_datetime + timedelta(hours=6), 1), # (initial_datetime + timedelta(hours=7), 1), # (initial_datetime + timedelta(hours=8), 1), # (initial_datetime + timedelta(hours=9), 1), # (initial_datetime + timedelta(hours=10), 1), # (initial_datetime + timedelta(hours=11), 1), # (initial_datetime + timedelta(hours=12), 1), # (initial_datetime + timedelta(hours=13), 1), # (initial_datetime + timedelta(hours=14), 1), # (initial_datetime + timedelta(hours=15), 1), # (initial_datetime + timedelta(hours=16), 1), # (initial_datetime + timedelta(hours=17), 1), # (initial_datetime + timedelta(hours=18), 1), # (initial_datetime + timedelta(hours=19), 1), # (initial_datetime + timedelta(hours=20), 1), # (initial_datetime + timedelta(hours=21), 1), # (initial_datetime + timedelta(hours=22), 1), # (initial_datetime + timedelta(hours=23), 1) # ] distribution_weighted_datetimes = [ (initial_datetime + timedelta(hours=i), travel_requests_simulator_datetime_distribution_weights[i]) for i in range(0, 24) ] datetime_population = [ val for val, cnt in distribution_weighted_datetimes for i in range(cnt) ] travel_request_documents = [] maximum_client_id = self.mongodb_database_connection.get_maximum_or_minimum( collection='travel_request') for i in range(0, number_of_travel_request_documents): client_id = maximum_client_id + 1 maximum_client_id = client_id starting_bus_stop_index = random.randint(0, number_of_bus_stops - 2) starting_bus_stop = bus_stops[starting_bus_stop_index] ending_bus_stop_index = random.randint(starting_bus_stop_index + 1, number_of_bus_stops - 1) ending_bus_stop = bus_stops[ending_bus_stop_index] additional_departure_time_interval = random.randint(0, 59) departure_datetime = ( random.choice(datetime_population) + timedelta(minutes=additional_departure_time_interval)) travel_request_document = { 'client_id': client_id, 'bus_line_id': bus_line_id, 'starting_bus_stop': starting_bus_stop, 'ending_bus_stop': ending_bus_stop, 'departure_datetime': departure_datetime, 'arrival_datetime': None, 'starting_timetable_entry_index': None, 'ending_timetable_entry_index': None } travel_request_documents.append(travel_request_document) # 4: The generated travel_request_documents are stored at the # TravelRequests collection of the System Database. # self.mongodb_database_connection.insert_travel_request_documents( travel_request_documents=travel_request_documents)