diff --git a/genet/core.py b/genet/core.py index 4952f09c..65efb46e 100644 --- a/genet/core.py +++ b/genet/core.py @@ -2126,6 +2126,7 @@ def generate_validation_report(self, modes_for_strong_connectivity=None, link_me logging.info('Checking validity of the Network') logging.info('Checking validity of the Network graph') report = {} + is_valid_network = True # describe network connectivity if modes_for_strong_connectivity is None: @@ -2135,6 +2136,8 @@ def generate_validation_report(self, modes_for_strong_connectivity=None, link_me graph_connectivity = {} for mode in modes_for_strong_connectivity: graph_connectivity[mode] = self.check_connectivity_for_mode(mode) + if graph_connectivity[mode]['number_of_connected_subgraphs'] not in {0, 1}: + is_valid_network = False report['graph'] = {'graph_connectivity': graph_connectivity} isolated_nodes = self.isolated_nodes() @@ -2144,6 +2147,7 @@ def generate_validation_report(self, modes_for_strong_connectivity=None, link_me } if self.has_isolated_nodes(): logging.warning('This Network has isolated nodes! Consider cleaning it up with `remove_isolated_nodes`') + is_valid_network = False # attribute checks conditions_toolbox = network_validation.ConditionsToolbox() @@ -2191,6 +2195,17 @@ def links_over_threshold_length(value): 'service_routes_with_invalid_network_route': self.invalid_network_routes(), 'route_to_crow_fly_ratio': route_to_crow_fly_ratio } + if not (report['routing']['services_have_routes_in_the_graph']): + is_valid_network = False + + report['intermodal_access_egress'] = { + 'has_valid_intermodal_connections': self.has_valid_intermodal_access_egress_connections(), + 'invalid_intermodal_connections': self.invalid_intermodal_access_egress_connections() + } + if not (report['schedule']['schedule_level']['is_valid_schedule'] and report['intermodal_access_egress'][ + 'has_valid_intermodal_connections']): + is_valid_network = False + report['is_valid_network'] = is_valid_network return report def report_on_link_attribute_condition(self, attribute, condition): diff --git a/tests/fixtures.py b/tests/fixtures.py index 16863f51..bf5b534f 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -258,30 +258,44 @@ def __init__(self): 'lat': 51.52228713323965, 's2_id': 5221390328605860387} }) - self.network.add_link('link_0', 'A', 'B', - attribs={ - 'id': 'link_0', - 'from': 'A', - 'to': 'B', - 'freespeed': 10, - 'capacity': 600, - 'permlanes': 1, - 'oneway': '1', - 'modes': ['car'], - 's2_from': 5221390329378179879, - 's2_to': 5221390328605860387, - 'length': 53 - }) + self.network.add_links({ + 'link_AB': { + 'id': 'link_AB', + 'from': 'A', + 'to': 'B', + 'freespeed': 10, + 'capacity': 600, + 'permlanes': 1, + 'oneway': '1', + 'modes': ['car', 'bus'], + 's2_from': 5221390329378179879, + 's2_to': 5221390328605860387, + 'length': 53 + }, + 'link_BA': { + 'id': 'link_BA', + 'from': 'B', + 'to': 'A', + 'freespeed': 10, + 'capacity': 600, + 'permlanes': 1, + 'oneway': '1', + 'modes': ['car', 'bus'], + 's2_from': 5221390329378179879, + 's2_to': 5221390328605860387, + 'length': 53 + } + }) self.network.schedule = Schedule(epsg='epsg:27700', services=[ Service(id='service', routes=[ Route(id='route', route_short_name='route', mode='bus', - stops=[Stop(id='Stop_A', x=528705, y=182069, epsg='epsg:27700', linkRefId='link_0'), - Stop(id='Stop_B', x=528836, y=182007, epsg='epsg:27700', linkRefId='link_0')], + stops=[Stop(id='Stop_A', x=528705, y=182069, epsg='epsg:27700', linkRefId='link_AB'), + Stop(id='Stop_B', x=528836, y=182007, epsg='epsg:27700', linkRefId='link_BA')], trips={'trip_id': ['trip_id_04:40:00'], 'trip_departure_time': ['04:40:00'], 'vehicle_id': ['veh_1_bus']}, - route=['link_0'], + route=['link_AB', 'link_BA'], arrival_offsets=['00:00:00', '00:02:00'], departure_offsets=['00:00:00', '00:02:00']) ]) @@ -316,14 +330,14 @@ def with_valid_car_intermodal_access_egress(self): new_stops_data = { 'Stop_A': { 'attributes': { - access_link_id_tag: 'link_0', + access_link_id_tag: 'link_AB', accessible_tag: 'true', distance_catchment_tag: 10 } }, 'Stop_B': { 'attributes': { - access_link_id_tag: 'link_0', + access_link_id_tag: 'link_BA', accessible_tag: 'true', distance_catchment_tag: 10 } @@ -332,7 +346,7 @@ def with_valid_car_intermodal_access_egress(self): self.schedule.apply_attributes_to_stops(new_stops_data) self.intermodal_access_egress_attribute_keys = [access_link_id_tag] self.intermodal_access_egress_connections_dataframe = pd.DataFrame( - {'attributes::accessLinkId_car': {'Stop_A': 'link_0', 'Stop_B': 'link_0'}}) + {'attributes::accessLinkId_car': {'Stop_A': 'link_AB', 'Stop_B': 'link_BA'}}) self.invalid_intermodal_access_egress_connections = { 'car': { 'stops_with_links_not_in_network': set(), @@ -355,7 +369,7 @@ def with_invalid_intermodal_access_egress(self): }, 'Stop_B': { 'attributes': { - access_link_id_tag: 'link_0', # stop with link that doesn't have the right mode on it + access_link_id_tag: 'link_BA', # stop with link that doesn't have the right mode on it accessible_tag: 'true', distance_catchment_tag: 10 } @@ -364,7 +378,7 @@ def with_invalid_intermodal_access_egress(self): self.schedule.apply_attributes_to_stops(new_stops_data) self.intermodal_access_egress_attribute_keys = [access_link_id_tag] self.intermodal_access_egress_connections_dataframe = pd.DataFrame( - {'attributes::accessLinkId_piggyback': {'Stop_A': 'non_existent_link', 'Stop_B': 'link_0'}}) + {'attributes::accessLinkId_piggyback': {'Stop_A': 'non_existent_link', 'Stop_B': 'link_BA'}}) self.invalid_intermodal_access_egress_connections = { 'piggyback': { 'stops_with_links_not_in_network': {'Stop_A'}, diff --git a/tests/test_core_network.py b/tests/test_core_network.py index 55788bd5..caa29e42 100644 --- a/tests/test_core_network.py +++ b/tests/test_core_network.py @@ -3132,6 +3132,18 @@ def test_nested_values_show_up_in_validation_report(): } ) +@pytest.mark.parametrize( + "fixture,is_valid_network", + [ + (NetworkForIntermodalAccessEgressTesting().without_intermodal_access_egress(), True), + (NetworkForIntermodalAccessEgressTesting().with_valid_car_intermodal_access_egress(), True), + (NetworkForIntermodalAccessEgressTesting().with_invalid_intermodal_access_egress(), False), + ] +) +def test_intermodal_access_egress_reporting(fixture, is_valid_network): + report = fixture.network.generate_validation_report() + assert report['is_valid_network'] == is_valid_network + def test_check_connectivity_for_mode_warns_of_graphs_with_more_than_single_component(mocker, caplog): mocker.patch.object(network_validation, 'describe_graph_connectivity',