└── Easy Rider Bus Company └── task └── easyrider └── easyrider.py /Easy Rider Bus Company/task/easyrider/easyrider.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | from datetime import datetime 4 | 5 | 6 | def check_pattern(data, type1): 7 | if type1 == "stop_name": 8 | pattern = r"[A-Z][\w\s]+(Road|Avenue|Boulevard|Street)$" 9 | elif type1 == "stop_type": 10 | pattern = r"[SOF]?$" 11 | elif type1 == "a_time": 12 | pattern = r"[0-2][0-9]:[0-5][0-9]$" 13 | 14 | return bool(re.match(pattern, data)) 15 | 16 | 17 | def errors_summary(list_of_dicts): 18 | errors_dictionary = {} 19 | 20 | for dictionary in list_of_dicts: 21 | for key, val in dictionary.items(): 22 | if key not in errors_dictionary: 23 | errors_dictionary.setdefault(key, 0) 24 | 25 | if key == "bus_id" and not isinstance(val, int): 26 | errors_dictionary["bus_id"] += 1 27 | 28 | elif key == "stop_id" and not isinstance(val, int): 29 | errors_dictionary["stop_id"] += 1 30 | 31 | elif key == "stop_name" and (not check_pattern(val, key) or not isinstance(val, str) or not len(val) > 1): 32 | errors_dictionary["stop_name"] += 1 33 | 34 | elif key == "next_stop" and not isinstance(val, int): 35 | errors_dictionary["next_stop"] += 1 36 | 37 | elif key == "stop_type" and (not check_pattern(val, key) or not isinstance(val, str) or len(val) > 1): 38 | errors_dictionary["stop_type"] += 1 39 | 40 | elif key == "a_time" and (not check_pattern(val, key) or not isinstance(val, str) or not len(val) > 1): 41 | errors_dictionary["a_time"] += 1 42 | errors_sum = sum(errors_dictionary.values()) 43 | if errors_sum > 0: 44 | print(f"Type and required field validation: {errors_sum} errors") 45 | print("\n".join([f"{key}: {val}" for key, val in errors_dictionary.items() if val > 0])) 46 | return False 47 | else: 48 | return True 49 | 50 | 51 | def count_stops(list_of_dicts): 52 | buses_stops_dictionary = {} 53 | 54 | for dictionary in list_of_dicts: 55 | if dictionary.get("bus_id") not in buses_stops_dictionary: 56 | buses_stops_dictionary.setdefault(dictionary.get("bus_id"), 1) 57 | else: 58 | buses_stops_dictionary[dictionary.get("bus_id")] += 1 59 | 60 | stops_sum = sum(buses_stops_dictionary.values()) 61 | if stops_sum > 0: 62 | print(f"Line names and number of stops:") 63 | print("\n".join([f"bus_id: {key}, stops: {val}" for key, val in buses_stops_dictionary.items() if val > 0])) 64 | 65 | 66 | def check_route(list_of_dicts): 67 | routes_stops_dictionary = {} 68 | report_dictionary = {} 69 | 70 | for dictionary in list_of_dicts: 71 | if dictionary.get("bus_id") not in routes_stops_dictionary: 72 | routes_stops_dictionary.setdefault(dictionary.get("bus_id"), []).append((dictionary.get("stop_type"), 73 | dictionary.get("stop_name"))) 74 | else: 75 | routes_stops_dictionary[dictionary.get("bus_id")].append( 76 | (dictionary.get("stop_type"), dictionary.get("stop_name"))) 77 | 78 | for key, val in routes_stops_dictionary.items(): 79 | if not all(any(stop_type in tup for tup in val) for stop_type in ["S", "F"]): 80 | print(f"There is no start or end stop for the line: {key}.") 81 | break 82 | 83 | list_of_streets = [] 84 | for dictionary in list_of_dicts: 85 | if dictionary["stop_type"] == "S": 86 | if not report_dictionary.get("Start stops"): 87 | report_dictionary.setdefault("Start stops", []).append(dictionary.get("stop_name")) 88 | elif dictionary.get("stop_name") not in report_dictionary["Start stops"]: 89 | report_dictionary.get("Start stops").append(dictionary.get("stop_name")) 90 | 91 | elif dictionary["stop_type"] == "F": 92 | if not report_dictionary.get("Finish stops"): 93 | report_dictionary.setdefault("Finish stops", []).append(dictionary.get("stop_name")) 94 | elif dictionary.get("stop_name") not in report_dictionary["Finish stops"]: 95 | report_dictionary.setdefault("Finish stops", []).append(dictionary.get("stop_name")) 96 | 97 | list_of_streets.append(dictionary.get("stop_name")) 98 | 99 | transfer_stops = set([street for street in list_of_streets if list_of_streets.count(street) > 1]) 100 | report_dictionary["Transfer stops"] = list(transfer_stops) 101 | 102 | # for key in ["Start stops", "Transfer stops", "Finish stops"]: 103 | # val = report_dictionary[key] 104 | # print(f"{key}: {len(val)} {sorted(val)}") 105 | 106 | return report_dictionary 107 | 108 | 109 | def arrival_time_test(list_of_dicts): 110 | time_format = "%H:%M" 111 | bus_id_dictionary = {} 112 | 113 | for dictionary in list_of_dicts: 114 | bus_id = dictionary.get('bus_id') 115 | next_stop = dictionary.get('next_stop') 116 | time_current_stop = datetime.strptime(dictionary.get('a_time'), time_format) 117 | if not bus_id_dictionary.get(bus_id): 118 | for dict_item in list_of_dicts: 119 | time_next_stop = datetime.strptime(dict_item['a_time'], time_format) 120 | if dict_item['bus_id'] == bus_id and dict_item['stop_id'] == next_stop and time_next_stop < time_current_stop: 121 | next_stop_id = dict_item.get('bus_id') 122 | next_stop_name = dict_item.get('stop_name') 123 | bus_id_dictionary.setdefault(bus_id, {'stop_id': next_stop_id, 'stop_name': next_stop_name}) 124 | 125 | print("Arrival time test:") 126 | if bus_id_dictionary: 127 | for key, dictionary in bus_id_dictionary.items(): 128 | bus_id = key 129 | stop_name = dictionary.get('stop_name') 130 | print(f"bus_id line {bus_id}: wrong time on station {stop_name}") 131 | else: 132 | print("OK") 133 | 134 | 135 | def on_demand_stop_test(report_dictionary, list_of_dicts): 136 | combined_list = [] 137 | wrong_stop_type = [] 138 | for values in report_dictionary.values(): 139 | combined_list.extend(values) 140 | combined_list = list(set(combined_list)) 141 | 142 | for dictionary in list_of_dicts: 143 | stop_name = dictionary.get('stop_name') 144 | if all([stop_name in combined_list, stop_name not in wrong_stop_type, dictionary.get('stop_type') == 'O']): 145 | wrong_stop_type.append(stop_name) 146 | 147 | print("On demand stops test:") 148 | if wrong_stop_type: 149 | print(f"Wrong stop type: {sorted(wrong_stop_type)}") 150 | else: 151 | print("OK") 152 | 153 | 154 | if __name__ == "__main__": 155 | string = input() 156 | 157 | list_of_dictionaries = json.loads(string) 158 | if errors_summary(list_of_dictionaries): 159 | # count_stops(list_of_dictionaries) 160 | report_dict = check_route(list_of_dictionaries) 161 | on_demand_stop_test(report_dict, list_of_dictionaries) 162 | # arrival_time_test(list_of_dictionaries) 163 | --------------------------------------------------------------------------------