def __get_trip_distances(self, trip): trip.od_distance = CommonHelper.point_distance(trip.origin, trip.destination) #TODO TEMP #if trip.od_distance == 0: # print("WARNING!!! in __get_trip_distances(): od_distance is 0, for trip ID:",trip.id," OD:",trip.origin, trip.destination) trip.legs_without_points = [] trip.distance = 0 trip.distance_by_mode = {} for leg in trip.legs: # distance by mode .....: # first calcualtes and SETs leg['distance'] values ... TODO!!! should be done elsewhere for example during leg-detection? if leg['is_moprim_leg']: pass #TODO: for legs retrieved from OTP, presumably, leg['distance'] is accurate enough (is point-to-point based) elif leg['is_otp_leg']: pass #TODO: for legs retrieved from OTP, presumably, leg['distance'] is accurate enough (is point-to-point based) else: #i.e: leg from original points recorded by user device leg['od_distance'] = CommonHelper.point_distance(CommonHelper.geoJSON_to_pointRow(leg['origin']), CommonHelper.geoJSON_to_pointRow(leg['destination'])) # Calc travelled-distance, point to point based here, OR Write a plsql-like function that does so on DB server leg_points_rows = self.legs_dal.get_leg_points(leg) leg['distance'] = self.__calculate_trajectory_distance(leg_points_rows) if leg_points_rows.rowcount == 0: log(["Warning in __get_trip_distances()!: legs_dal.get_leg_points(leg) returned ZERO points. For leg ("+str(leg['user_id'])+' '+ str(trip.id)+" "+str(leg['id'])+ "; device_id="+str(leg['device_id'])+'; '+str(leg['time_start'])+', '+str(leg['time_end'])]) trip.legs_without_points.append(leg) # add up leg-distances to total trip.distance: trip.add_travelled_distance(leg['mode'], leg['distance']) # NOTE: 'total distance' of trip is increased inside this function
def send_http_get(self, apiurl, querystr=None, verify_certificate=False): # Notes for urllib2: # GET request with urllib2: If you do not pass the data argument, urllib2 uses a GET request (you can encode the data in URL itself) # POST request with urllib2: Just as easy as GET, simply passing (encoded) request data as an extra parameter #params = urllib.urlencode(querystr) apiurl_with_query = apiurl params = querystr if params is not None and params != '': apiurl_with_query += "?" + params #log(["params:", params]) #log(["apiurl_with_query:", apiurl_with_query, "verify_certificate:", verify_certificate]) response = None res = False e = None exception_type = "" try: #response = urllib2.urlopen(apiurl_with_query) log(["send_http_get():", apiurl_with_query]) response = requests.get( apiurl_with_query, verify=verify_certificate ) # //TODO: possible security issue! verify=True works from python command line !!!! response.close() if response.status_code == 200: #TODO: does it happen that status_code is NOT CHANGED?? res = True except urllib.error.HTTPError as e: exception_type = "urllib.error.HTTPError" except requests.exceptions.ConnectionError as e: exception_type = "requests.exceptions.ConnectionError" except Exception as e: exception_type = "Exception" finally: pass # TODO, don't like the idea of finally changing program exec and exception raise path (use later, maybe) if res: response_str = response.content elif e is not None: response_str = "(!) EXCEPTION catched (class_type: {}) in WebNetworking::send_http_get(): ".format( exception_type) if response is not None: logexc(response_str, e, [ ", apiurl_with_query: ", apiurl_with_query, ", response content_type:", response.headers['content-type'] ]) else: logexc(response_str, e, [", apiurl_with_query: ", apiurl_with_query]) else: response_str = "! error in send_http_get(), res=False, response.status_code={}".format( response.status_code) loge([ "! error in send_http_get(), res=False, response.status_code=" ], response.status_code) pass #probably http error occured (eg status code != 200) return res, response_str
def load_trips(self, ids_to_process, date_range_start, date_range_end, timeofday_start, timeofday_end, timeofday_start2, timeofday_end2, min_od_distance, od_d_diff_coeff): # TODO: refine more if needed here qstr = """SELECT user_id, device_id, id, plan_id, start_time, end_time, ST_AsGeoJSON(origin) as origin, ST_AsGeoJSON(destination) as destination, multimodal_summary, duration, cost, calories, emission, comfort, distance, time_by_mode, cost_by_mode, calories_by_mode, emission_by_mode, distance_by_mode, mainmode, start_time_for_plan, notes, od_distance FROM trips_alts WHERE (user_id, id) IN ( SELECT user_id, id FROM trips_alts WHERE user_id IN({0}) AND plan_id = 0 AND start_time >= '{1}' AND end_time <= '{2}' AND ((start_time::time >= time {3} AND end_time::time <= time {4}) OR (start_time::time >= time {5} AND end_time::time <= time {6})) AND ST_Distance(origin, destination) > {7} AND distance < {8} * ST_Distance(origin, destination) ) ORDER BY user_id, id, plan_id; """.format(ids_to_process, DateTime_to_Text(date_range_start), DateTime_to_Text(date_range_end), DateTime_to_SqlText(timeofday_start), DateTime_to_SqlText(timeofday_end), DateTime_to_SqlText(timeofday_start2), DateTime_to_SqlText(timeofday_end2), min_od_distance, od_d_diff_coeff) log(["load_trips():: qstr:", qstr]) log("") res, trip_rows = self.db_command.ExecuteSQL(qstr) total_trip_and_alt_count = trip_rows.rowcount trips = [] if trip_rows.rowcount > 0: actualtrip = None for trip_row in trip_rows: trip = Trip(trip_row) # OLD Code : Parse JSON into an object with attributes corresponding to dict keys # TODO: WARNING!!: (?) only works well if names of DB table columns are same as names of our Python Class attributes # trip.__dict__= json.loads(trip_row['trip_as_json']) # TODO: IMPORTANT !!!: load trip legs * Needed?? # ... # build the nested structure of 'trips' list in our program: if trip.plan_id == 0: actualtrip = Trip() actualtrip = deepcopy( trip) # TODO: WARNING !!! is this neede?!! trips.append(actualtrip) elif trip.plan_id > 0: if actualtrip is not None: actualtrip.alternative_trips.append(trip) # log(trip.user_id, trip.id, trip.plan_id, trip.origin) return trips
def find_same_journey_time_this_week(cls, original_date_time): # find date of the same weekday, but for current week (to avoid querying old dates that results in error from HSL) if original_date_time.weekday() < datetime.today().weekday(): reference_date = datetime.today() + timedelta(weeks = 1) else: reference_date = datetime.today() date_thisweek = reference_date + timedelta(days = (original_date_time.weekday() - reference_date.weekday())) time_thisweek = datetime.combine(date_thisweek.date(), original_date_time.time()) log(["find_same_journey_time_this_week():: original_date_time:"+DateTime_to_Text(original_date_time)+ " --> same journey time this or next week:" + DateTime_to_Text(time_thisweek) ]) return time_thisweek
def ExecuteSQL(self, query_string, LOG_IMPORTANT_CUSTOM=True): qstr = query_string log(["DatabaseCommands::ExecuteSQL() qstr:", qstr]) db_res = None res = False try: db_res = self.db_engine.execute(text(qstr)) res = True except Exception as e: self._log_exception( "DatabaseCommandsNonTransactional::ExecuteSQL()", qstr, str(e)) return res, db_res
def find_trip_od_for_recorded_location(self, recorded_geoloc, distance_inaccuracy_threshold): qstr = """SELECT geoloc FROM trip_origin_destinations WHERE ST_Distance(geoloc, ST_GeomFromText('{0}')) <= {1} """.format(pointRow_to_postgisPoint(recorded_geoloc), distance_inaccuracy_threshold) log(["find_trip_od_for_recorded_location():: qstr:", qstr]) log("") res, trip_od_rows = self.db_command.ExecuteSQL(qstr) if trip_od_rows.rowcount > 0: for trip_od_row in trip_od_rows: return trip_od_row['geoloc'] return None
def ExecuteSQL(self, query_string): qstr = query_string log(["DatabaseCommandsTransactional::ExecuteSQL() qstr:", qstr]) db_res = None res = False try: db_res = self.session.execute( qstr) # transaction starts here, if not started before res = True except Exception as e: self._log_exception("DatabaseCommandsTransactional::ExecuteSQL()", qstr, str(e)) raise e #TODO NOTE! Crucial; So that it is passed eventually to BLL level that will call rollback() if needed # TODO: But, for an easier approach, shouldn't this function itself Rollback the transaction?? return res, db_res
def get_next_user_trip_id(self, user_id, date_range_start, date_range_end): qstr = """SELECT max(id) as max_id FROM trips_alts WHERE user_id = {0} AND (user_id, id, plan_id) NOT IN (SELECT user_id, id, plan_id FROM trips_alts WHERE user_id = {0} AND start_time >= '{1}' AND end_time <= '{2}'); """.format(user_id, CommonHelper.DateTime_to_Text(date_range_start), CommonHelper.DateTime_to_Text(date_range_end)) log(["get_next_user_trip_id():: qstr:", qstr]) log("") res, maxid_rows = self.db_command.ExecuteSQL(qstr) if maxid_rows.rowcount > 0: for maxid_row in maxid_rows: if maxid_row['max_id'] == None: return 0 else: return maxid_row['max_id'] return 0
def store_trip_od_location(self, recorded_geoloc, distance_inaccuracy_threshold): qstr = """INSERT INTO trip_origin_destinations (geoloc, distance_inaccuracy_threshold) VALUES (ST_GeomFromText('{0}'), {1}) """.format(pointRow_to_postgisPoint(recorded_geoloc), distance_inaccuracy_threshold) log(["store_trip_od_location:: qstr:", qstr]) log("") res, db_res = self.db_command.ExecuteSQL(qstr) log(["result:", str(res)]) return res
def delete_trips_by_id(self, ids_to_process, trip_id_start, trip_id_end): qstr = """DELETE FROM trips_alts WHERE user_id IN ({0}) AND id >= '{1}' AND id <= '{2}'; """.format(ids_to_process, trip_id_start, trip_id_end) log([ "delete_trips():: qstr for removing old rows before storing new ones:", qstr ]) res, db_res = self.db_command.ExecuteSQL(qstr) log(["result:", res]) log("") return res
def delete_trips_and_alternatives(self, date_range_start, date_range_end): qstr = """DELETE FROM trips_alts WHERE true AND start_time >= '{}' AND end_time <= '{}'; """.format(DateTime_to_Text(date_range_start), DateTime_to_Text(date_range_end)) log([ "delete_trips_and_alternatives():: qstr for removing old rows before storing new ones:", qstr ]) res, db_res = self.db_command.ExecuteSQL(qstr) log(["result:", res]) log("") return res
def delete_trips(self, ids_to_process, date_range_start, date_range_end): qstr = """DELETE FROM trips_alts WHERE user_id IN ({0}) AND start_time >= '{1}' AND end_time <= '{2}'; """.format(ids_to_process, DateTime_to_Text(date_range_start), DateTime_to_Text(date_range_end)) log([ "delete_trips():: qstr for removing old rows before storing new ones:", qstr ]) res, db_res = self.db_command.ExecuteSQL(qstr) log(["result:", res]) log("") return res
def __increase_modalchoice_for_user(self, user_id, trip, user_modalchoice_list): log(["increase_modalchoice_for_user:: ..."]) log(["trip.mainmode:", trip.mainmode]) chosenmode = trip.mainmode if chosenmode in PublicTransportModesTemplate: chosenmode = 'PUBLIC_TRANSPORT' log(["chosenmode:", chosenmode]) if user_id not in user_modalchoice_list: user_modalchoice_list[user_id] = deepcopy(UserModalChoiceTemplate) if chosenmode in user_modalchoice_list[user_id]: user_modalchoice_list[user_id][chosenmode] += 1
def store_trip_to_leg(self, trip, leg): qstr = "" try: qstr = """INSERT INTO trips_to_legs (user_id, trip_id, plan_id, leg_id) VALUES ({0},{1},{2},{3})""".format(trip.user_id, trip.id, trip.plan_id, leg['id']) log(["store_trip_to_leg():: qstr:", qstr]) log("") res, db_res = self.db_command.ExecuteSQL(qstr) log(["result:", str(res)]) except Exception as e: print("Exception in store_trip_to_leg():") print("exception:", e) print("qstr:", qstr) raise #TODO NOTE critical .. so that BLL is able to rollback if needed *
def _query_a_trip_plan(self, desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops): log("") log(["_query_a_trip_plan::Requesting to OTP journey planner:"]) log([ "Input desired_trip:", desired_trip.starttime, ",", desired_transport_mode ]) # Send our query to OTP journey planner: # Example: # querystr = "fromPlace=60.170718,24.930221&toPlace=60.250214,25.009566&date=2016/4/22&time=17:18:00&numItineraries=3&maxTransfers=3&maxWalkDistance=1500" if desired_transport_mode == 'PUBLIC_TRANSPORT' or desired_transport_mode in PublicTransportModesTemplate: querystr = """fromPlace={0}&toPlace={1}&date={2}&time={3}&numItineraries={4}&maxWalkDistance={5}&showIntermediateStops={6}""".format( pointRow_to_geoText(desired_trip.origin), pointRow_to_geoText(desired_trip.destination), desired_trip.starttime.date().isoformat(), desired_trip.starttime.time().isoformat(), num_of_itineraries, max_walk_distance, show_intermediate_stops) else: querystr = """fromPlace={0}&toPlace={1}&date={2}&time={3}&numItineraries={4}&mode={5}""".format( pointRow_to_geoText(desired_trip.origin), pointRow_to_geoText(desired_trip.destination), desired_trip.starttime.date().isoformat(), desired_trip.starttime.time().isoformat(), num_of_itineraries, desired_transport_mode) webres, response_content_str = self.webnet.send_http_get( self.api_url, querystr) if type(response_content_str) == bytes: response_content_str = response_content_str.decode() return self._parse_webnet_response(webres, response_content_str)
def increase_pros_for_user(self, user_id, comparison_list, cl_desired_template, best_modalchoice_by_param): log([""]) log(["increase_pros_for_user ...:"]) log(["cl_desired_template:", cl_desired_template]) log(["best_modalchoice_by_param:", best_modalchoice_by_param]) if user_id not in comparison_list: comparison_list[user_id] = deepcopy(cl_desired_template) log(["comparison_list before calcs:", comparison_list]) for param, choice in best_modalchoice_by_param.iteritems(): if choice['mode'] is not None: comparison_list[user_id][param][choice['mode']] += 1 log(["comparison_list after calcs:", comparison_list])
def get_best_modalchoice_by_param_for_trip(self, trip, desired_modes_template): # find the best modal-choice per trip depending on the target param (eg. 'time', 'cals', ...): mintime = None # TODO: or compare with original trip?! mincost = None maxcals = None minemission = None maxcomfort = None pros = deepcopy(BestModalChoiceByParamTemplate) # pros_detailed = deepcopy(BestModalChoiceByParamTemplate) # TODO later modes_to_compare = [] for mode, val in desired_modes_template.iteritems(): # if mode == 'PUBLIC_TRANSPORT': # for public_transport_mode in PublicTransportModesTemplate: # modes_to_compare.append(mode) modes_to_compare.append(mode) log([""]) log(["get_best_modalchoice_by_param_for_trip:: ...:"]) log(["modes_to_compare:", modes_to_compare]) #if trip.user_id not in comparison_list: # comparison_list[trip.user_id] = desiredCLTemplate #TODO old code?! remove? # find the min val or max val depending on the target param # (eg. for 'cals' we should find the tripalt (modal-choice chain) with largest 'cals' val) for tripalt in trip.alternative_trips: log(["tripalt.mainmode:", tripalt.mainmode]) mainmode = tripalt.mainmode if mainmode in PublicTransportModesTemplate: mainmode = 'PUBLIC_TRANSPORT' log(["mainmode:", mainmode]) if mainmode in modes_to_compare: if mintime is None or tripalt.duration < mintime: #TODO : what if == ??? mintime = tripalt.duration pros['time'] = { "plan_id": tripalt.plan_id, "mode": mainmode } if mincost is None or tripalt.cost < mincost: mincost = tripalt.cost pros['cost'] = { "plan_id": tripalt.plan_id, "mode": mainmode } if maxcals is None or tripalt.calories > maxcals: maxcals = tripalt.calories pros['cals'] = { "plan_id": tripalt.plan_id, "mode": mainmode } if minemission is None or tripalt.emission < minemission: minemission = tripalt.emission pros['emission'] = { "plan_id": tripalt.plan_id, "mode": mainmode } if maxcomfort is None or tripalt.comfort > maxcomfort: maxcomfort = tripalt.comfort pros['comfort'] = { "plan_id": tripalt.plan_id, "mode": mainmode } log([trip.user_id, trip.device_id, trip.id, ":", " pros:", pros]) return pros
def store_trip(self, trip): # NOTE: example INSERT: insert into mytest (name) VALUES ('{''car'', ''walk''}'); res = "" qstr = "" try: qstr = """INSERT INTO trips_alts (user_id, device_id, id, plan_id, start_time, end_time, origin, destination, multimodal_summary, duration, cost, calories, emission, comfort, distance, time_by_mode, cost_by_mode, calories_by_mode, emission_by_mode, distance_by_mode, mainmode, start_time_for_plan, od_distance) VALUES ({0},{1},{2},{3},'{4}','{5}', ST_GeomFromText('{6}'), ST_GeomFromText('{7}'), '{8}', '{9}',{10},{11},{12},{13},{14}, '{15}','{16}','{17}','{18}','{19}', '{20}', {21},{22})""".format( trip.user_id, trip.device_id, trip.id, trip.plan_id, DateTime_to_Text(trip.starttime), DateTime_to_Text(trip.endtime), pointRow_to_postgisPoint(trip.origin), pointRow_to_postgisPoint(trip.destination), trip.multimodal_summary, DateTimeDelta_to_Text(trip.duration), round(trip.cost, 2), int(round(trip.calories)), round(trip.emission, 1), # TODO, old: round(trip.emission/1000.0, 1), trip.comfort, round(trip.distance, 1), # TODO json.dumps(dict_timedelta_to_text(trip.duration_by_mode) ), #TODO, test and verify all following json.dumps(round_dict_values(trip.cost_by_mode, 2)), json.dumps(round_dict_values(trip.calories_by_mode, 2)), json.dumps(round_dict_values(trip.emission_by_mode, 2)), json.dumps(round_dict_values(trip.distance_by_mode, 2)), trip.mainmode, DateTime_to_SqlText( trip.shifted_starttime_for_publictransport_tripplan), trip.od_distance) #round_dict_values(trip.duration_by_mode, 2), \ #round_dict_values(trip.cost_by_mode, 2), round_dict_values(trip.calories_by_mode, 0), \ #round_dict_values(trip.emission_by_mode, 0), round_dict_values(trip.distance_by_mode, 0),\ #pointRow_to_geoText(trip.origin), pointRow_to_geoText(trip.destination) except Exception as e: print("") print(">> store_trip():: FAILED ------------------------------") print(">> trip to store:", trip) print("exception: ", e) print("") raise #TODO NOTE critical .. so that BLL is able to rollback if needed * try: res, db_res = self.db_command.ExecuteSQL(qstr) except Exception as e: print("") print(">> store_trip():: FAILED ------------------------------") print(">> trip to store:", trip) print("exception: ", e) print("qstr: ", qstr) print("") raise #TODO NOTE critical .. so that BLL is able to rollback if needed * log(["result:", str(res)]) return res
def plan_a_trip(self, reference_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops): log("") log(["plan_a_trip::Input reference trip: ", reference_trip.starttime, \ ": from ", pointRow_to_geoText(reference_trip.origin), "--> to", pointRow_to_geoText(reference_trip.destination)]) desired_trip = deepcopy( reference_trip ) # keep a copy, in order to not change the input params in the caller's scope desired_trip.starttime = desired_trip.starttime.replace(microsecond=0) desired_trip.endtime = desired_trip.endtime.replace(microsecond=0) # query journey planner --------------: is_desired_trip_date_shifted = False shifted_starttime = None plan = None res = 0 error = None planning_response = None restored_plan = StoredPlanLoadResult() # if such OTP plan is (requested and) stored before, just use it! if not self.config.RETRY_ON_ALL_STORED_PLANS: #TODO important, later, possible BUG: if 'itin_starttime' (planned) differs a bit from the stored 'desired_trip.starttime' (desired) ... some plans may get lost ??! # one solution: use the requestedParameters['date'] and ['time'] instead of values from itin ??? restored_plan = self._restore_a_trip_plan_from_cache( desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance) res = restored_plan.res plan = restored_plan.plan error = restored_plan.error planning_response = restored_plan.planning_response is_desired_trip_date_shifted = restored_plan.date_was_shifted_forward shifted_starttime = restored_plan.shifted_starttime # depending on config flags and other conditions: # . if flag set: requery regardless of whether we already have the plan in cache # . if not loaded from previous plans # . .... # send trip-planning query to OTP server: if self.config.RETRY_ON_ALL_STORED_PLANS or \ (not restored_plan.plan_made_before and \ ((not restored_plan.is_otp_error) or \ (self.config.RETRY_ON_ALL_STORED_OTP_ERRORS) or\ (self.config.RETRY_ON_STORED_OTP_ERROR_DATE_TOO_FAR and \ (restored_plan.error['id'] == OTP_ERROR_CODE_DATE_TOO_FAR or restored_plan.error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE))\ )\ ): # TODO: important changes made to the above id condition during moprim (both 404 and 406 errors treated thesame) res, plan, error, all_response = self._query_a_trip_plan( desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops) # TODO: WARNING! dummy test only, to help regenerate the error: ValueError('No JSON object could be decoded',) is not JSON serializable # error['id'] = OTP_ERROR_CODE_DATE_TOO_FAR if res == 0 \ and (error['id'] == OTP_ERROR_CODE_DATE_TOO_FAR \ or error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE): #TODO: OTP_ERROR_CODE_NO_PATH_POSSIBLE added during MOprim data processing. Both 404 and 406 errors are now somehow synonyms # second try (adjust the old weekday to current week) #store this 'intermediate' error trip-plan response for later use *** self._store_planning_error(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) # shift the old weekday to current week, and call OTP planner again logi([ "plan_a_trip():: FAILED for trip (" + str(desired_trip.user_id) + " " + str(desired_trip.id) + ") -" + desired_transport_mode + ": OTP error code=", error['id'] + "; Trying a second time with shifted date to current week..." ]) is_desired_trip_date_shifted = True # *** desired_trip_with_shifted_date = self._shift_trip_to_current_week( desired_trip ) # TODO: function algorithm has changed during Moprim ... probably suitable for other TrafficSense data sources too shifted_starttime = desired_trip_with_shifted_date.starttime # OLD CODE: # desired_trip_with_shifted_date = deepcopy(desired_trip) # desired_trip_with_shifted_date.starttime = self.find_same_journey_time_this_week(desired_trip.starttime) # desired_trip_with_shifted_date.endtime = self.find_same_journey_time_this_week(desired_trip.endtime) res, plan, error, all_response = self._query_a_trip_plan( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops) if res == 1: #store this 'shifted' trip-plan-response for later use *** self._store_planning_result(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) log(["plan_a_trip() SUCCESS with shifted time"]) elif res == 0 and self._has_trip_planning_error( all_response ): # only save errors related to trip planning, OTP and so on (NOT the network, exceptions, own custom etc. errors) #store this error trip-plan response for later use *** self._store_planning_error(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) elif res == 0 and self._has_trip_planning_error(all_response): #store this error trip-plan response for later use *** self._store_planning_error(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) elif res == 1: #store this trip-plan-response for later use *** self._store_planning_result(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) # summarize after trip planning is done: ------------------- if res == 1 and (plan is None or 'itineraries' not in plan): if plan is not None: raise Exception("res == 1 but plan is: {}".format( json.dumps(plan))) else: raise Exception('res == 1 but plan is None') trip_planning_res = TripPlanningResult() if res == 0: if error is not None: trip_planning_res.error_code = error['id'] trip_planning_res.error_msg = error['msg'] trip_planning_res.error_message = error['message'] return 0, None, trip_planning_res else: return 0, None, trip_planning_res elif res == 1: if is_desired_trip_date_shifted: trip_planning_res.is_desired_trip_date_shifted = is_desired_trip_date_shifted trip_planning_res.desired_trip_shifted_starttime = shifted_starttime # build trip objects to return to caller: -------------------------- # go through all trip-leg-chains suggested, to build a collection of Trip objects, and return it : log("") log([ "Working on the itins (routes) suggested by otp journey planner ...:" ]) itin_index = 0 matchcount = 0 plannedmatches = [] # TODO: choose which returned trip? # e.g. when 3 public transport trips are returned * order based on what? trips = [] for itin in plan['itineraries']: trip = Trip() trip.update_from_otp_trip_plan(desired_trip, is_desired_trip_date_shifted, plan, itin) # TODO: WARNING!! unit/integrate test. # Is the diff in 'trips_alts' table because of following change?! ****************** # trip.user_id = desired_trip.user_id # trip.device_id = desired_trip.device_id # trip.id = desired_trip.id # itin_starttime = OTPTimeStampToNormalDateTime(itin['startTime']) # itin_endtime = OTPTimeStampToNormalDateTime(itin['endTime']) # # trip.shifted_starttime_for_publictransport_tripplan = itin_starttime # no need to use this field for alternative plans? (plan_id > 0) # if is_desired_trip_date_shifted: # trip.starttime = shift_time_to_specific_date(itin_starttime, desired_trip.starttime) # TODO: NOTE: starttime of plan may differ from desired trip start-time (???) # trip.endtime = shift_time_to_specific_date(itin_endtime, desired_trip.endtime) # else: # trip.starttime = itin_starttime # TODO: NOTE: starttime.time()/endtime of planned itinerary may differ *a bit* from desired trip start-time.time/endtime (???) # trip.endtime = itin_endtime # trip.origin = geoLoc_to_pointRow(plan['from']['lat'], plan['from']['lon']) # TODO: are there cases where plan's origin{lat,lon} differ a bit from desired trip origin?! # trip.destination = geoLoc_to_pointRow(plan['to']['lat'], plan['to']['lon']) # TODO: are there cases where plan's destination{lat,lon} differ a bit from desired trip destination?! # # trip.legs = itin['legs'] #TODO remove old code? # trip.append_otp_legs(itin['legs'], is_desired_trip_date_shifted, desired_trip.starttime, desired_trip.endtime) trips.append(trip) return 1, trips, trip_planning_res
def plan_a_trip_new(self, reference_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops): log("") log(["plan_a_trip::Input reference trip: ", reference_trip.starttime, \ ": from ", pointRow_to_geoText(reference_trip.origin), "--> to", pointRow_to_geoText(reference_trip.destination)]) desired_trip = deepcopy( reference_trip ) # keep a copy, in order to not change the input params in the caller's scope # query journey planner --------------: is_desired_trip_date_shifted = False shifted_starttime = None plan = None res = 0 error = None planning_response = None restored_plan = StoredPlanLoadResult() if self.config.PLAN_ONLY_WITH_SHIFTED_DATE: # shift the old weekday to current week, and call OTP planner again logi([ "plan_a_trip_new() Force shifting date to current week, for trip (" + str(desired_trip.user_id) + " " + str(desired_trip.id) + ") -" + desired_transport_mode ]) is_desired_trip_date_shifted = True # *** desired_trip_with_shifted_date = self._shift_trip_to_current_week( desired_trip) shifted_starttime = desired_trip_with_shifted_date.starttime res, plan, error, all_response = self._query_a_trip_plan( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops) if res == 1: #store this 'shifted' trip-plan-response for later use *** self._store_planning_result(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) logi(["plan_a_trip_new() SUCCESS with shifted time"]) elif res == 0 and self._has_trip_planning_error( all_response ): # only save errors related to trip planning, OTP and so on (NOT the network, exceptions, own custom etc. errors) #store this error trip-plan response for later use *** self._store_planning_error(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) else: # if such OTP plan is (requested and) stored before, just use it! if not self.config.RETRY_ON_ALL_STORED_PLANS: #TODO important, later, possible BUG: if 'itin_starttime' (planned) differs a bit from the stored 'desired_trip.starttime' (desired) ... some plans may get lost ??! # one solution: use the requestedParameters['date'] and ['time'] instead of values from itin ??? restored_plan = self._restore_a_trip_plan_from_cache( desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance) res = restored_plan.res plan = restored_plan.plan error = restored_plan.error planning_response = restored_plan.planning_response is_desired_trip_date_shifted = restored_plan.date_was_shifted_forward shifted_starttime = restored_plan.shifted_starttime if restored_plan.already_restored_the_shifted_date_plan: print( "NOTE: restored_plan.already_restored_the_shifted_date_plan" ) # depending on config flags and other conditions: # . if flag set: requery regardless of whether we already have the plan in cache # . if not loaded from previous plans # . .... # send trip-planning query to OTP server: if self.config.RETRY_ON_ALL_STORED_PLANS or \ (not restored_plan.plan_made_before and \ ((not restored_plan.is_otp_error) or (self.config.RETRY_ON_ALL_STORED_OTP_ERRORS) or\ (self.config.RETRY_ON_STORED_OTP_ERROR_DATE_TOO_FAR and \ (not restored_plan.already_restored_the_shifted_date_plan) and\ (restored_plan.error['id'] == OTP_ERROR_CODE_DATE_TOO_FAR\ # TODO: NOTE: shifting date to current week only is useful for PT trips *** or (restored_plan.error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE and desired_transport_mode=='PUBLIC_TRANSPORT') ) )\ )\ ): # TODO: important changes made to the above id condition during moprim (both 404 and 406 errors treated thesame) res, plan, error, all_response = self._query_a_trip_plan( desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops) # TODO: WARNING! dummy test only, to help regenerate the error: ValueError('No JSON object could be decoded',) is not JSON serializable # error['id'] = OTP_ERROR_CODE_DATE_TOO_FAR if res == 0 \ and (error['id'] == OTP_ERROR_CODE_DATE_TOO_FAR \ #TODO: OTP_ERROR_CODE_NO_PATH_POSSIBLE added during MOprim data processing. Both 404 and 406 errors are now somehow synonyms fot PT # TODO: NOTE: shifting date to current week only is useful for PT trips *** or (error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE and desired_transport_mode=='PUBLIC_TRANSPORT') ): # second try (adjust the old weekday to current week) --------------------- #store this 'intermediate' error trip-plan response for later use *** self._store_planning_error(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) # shift the old weekday to current week, and call OTP planner again logi([ "plan_a_trip_new() FAILED for trip (" + str(desired_trip.user_id) + " " + str(desired_trip.id) + ") -" + desired_transport_mode + ": OTP ERROR CODE=" + str(error['id']) + "! => Trying second time with current week..." ]) is_desired_trip_date_shifted = True # *** desired_trip_with_shifted_date = self._shift_trip_to_current_week( desired_trip ) # TODO: function algorithm has changed during Moprim ... probably suitable for other TrafficSense data sources too shifted_starttime = desired_trip_with_shifted_date.starttime res, plan, error, all_response = self._query_a_trip_plan( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, show_intermediate_stops) if res == 1: #store this 'shifted' trip-plan-response for later use *** self._store_planning_result( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) log(["plan_a_trip_new() SUCCESS with shifted time"]) elif res == 0 and self._has_trip_planning_error( all_response ): # only save errors related to trip planning, OTP and so on (NOT the network, exceptions, own custom etc. errors) #store this error trip-plan response for later use *** self._store_planning_error( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) elif res == 0 and self._has_trip_planning_error(all_response): #store this error trip-plan response for later use *** self._store_planning_error(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) elif res == 1: #store this trip-plan-response for later use *** self._store_planning_result(desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance, all_response) # summarize after trip planning is done: ------------------- if res == 1 and (plan is None or 'itineraries' not in plan): if plan is not None: raise Exception("res == 1 but plan is: {}".format( json.dumps(plan))) else: raise Exception('res == 1 but plan is None') trip_planning_res = TripPlanningResult() # TODO: Important change: causes that shifted date is saved in trips_alts DB table, later in code if is_desired_trip_date_shifted: trip_planning_res.is_desired_trip_date_shifted = is_desired_trip_date_shifted trip_planning_res.desired_trip_shifted_starttime = shifted_starttime if res == 0: if error is not None: trip_planning_res.error_code = error['id'] trip_planning_res.error_msg = error['msg'] trip_planning_res.error_message = error['message'] return 0, None, trip_planning_res else: return 0, None, trip_planning_res elif res == 1: return 1, plan, trip_planning_res
def _restore_a_trip_plan_from_cache(self, desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance): is_desired_trip_date_shifted = False desired_trip_with_shifted_date = None is_otp_error = False plan_made_before = False already_restored_the_shifted_date_plan = False #TODO important, later, possible BUG: if 'itin_starttime' (planned) differs a bit from the stored 'desired_trip.starttime' (desired) ... some plans may get lost ??! # one solution: use the requestedParameters['date'] and ['time'] instead of values from itin ??? res, plan, error, planning_response = self._load_plan_already_stored( desired_trip, desired_transport_mode, num_of_itineraries, max_walk_distance) #print("First try of loading plan from DB; res = ", res) # TODO. Changes made during Moprim #if res == 0 and error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE: # pass # second try (adjust the old weekday to current week) from DB if res == 0 and (error['id'] == OTP_ERROR_CODE_DATE_TOO_FAR \ #TODO: important Moprim changes! # TODO: NOTE: shifting to current week only is useful for PT trips *** or (error['id'] == OTP_ERROR_CODE_NO_PATH_POSSIBLE and desired_transport_mode=='PUBLIC_TRANSPORT') ): log([ "_restore_a_trip_plan_from_cache(): for trip (" + str(desired_trip.user_id) + " " + str(desired_trip.id) + "), mode " + desired_transport_mode, "load from DB successful.", "But trip planning HAD FAILED because loaded record has: OTP error (error['id']={})" .format(error['id']) ]) log([ "Loading second time from DB, with the shifted date (either 'trips.shifted_starttime_for_publictransport_tripplan' or time current week) ..." ]) is_desired_trip_date_shifted = True # *** desired_trip_with_shifted_date = deepcopy(desired_trip) if desired_trip.shifted_starttime_for_publictransport_tripplan is not None: # if already shifted in a previous run, load from DB record values #print("TEMP MESSAGE: references-trip shifted-date for PT planning:", desired_trip.shifted_starttime_for_publictransport_tripplan) desired_trip_with_shifted_date.starttime = desired_trip.shifted_starttime_for_publictransport_tripplan else: # if not already shifted, do it here, try for same weekday in current week print( "Warning! References-trip shifted-date for PT planning NULL; Shifting the date here!!!" ) desired_trip_with_shifted_date.starttime = self.find_same_journey_time_this_week( desired_trip.starttime) # update the end-time too, for backwards compatibility desired_trip_with_shifted_date.endtime = shift_time_to_specific_date( desired_trip.endtime, desired_trip_with_shifted_date.starttime ) #TODO ... no separate data field for endtime ?!!! #print("TEMP MESSAGE: desired_trip_with_shifted_date:", desired_trip_with_shifted_date.starttime) old_error = error old_planning_response = planning_response res, plan, error, planning_response = self._load_plan_already_stored( desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance) if res: log([ "SUCCESS: date shifted-forward trip plan found in DB ==> using it ..." ]) # TODO: WARNING! test dummy use only if needed to REMOVE trip-plans for test!!! # TODO: WARNING! self.trip_plans_dal.delete_trip_plan(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance) elif self._has_trip_planning_error(planning_response): already_restored_the_shifted_date_plan = True logi([ "ERROR: date shifted-forward trip plan found; But trip planning HAD FAILED again because (error['id']={})" .format(error['id']) ]) # TODO: WARNING! test dummy use only if needed to REMOVE trip-plans for test!!! # TODO: WARNING! self.trip_plans_dal.delete_trip_plan(desired_trip_with_shifted_date, desired_transport_mode, num_of_itineraries, max_walk_distance) elif error['id'] == 0: # restore original error id and message error = old_error planning_response = old_planning_response loge(["ERROR: date shifted-forward trip plan NOT found in DB"]) #loge([""]) #TODO!!! revert # summarize the result: if res == 0 and self._has_trip_planning_error(planning_response): error['message'] = "_restore_a_trip_plan_from_cache() - " + error[ 'message'] is_otp_error = True elif res == 1: plan_made_before = True self._increase_no_of_correct_plans_loaded_from_cache() restore_res = StoredPlanLoadResult() restore_res.set(res, plan, error, planning_response, is_desired_trip_date_shifted, desired_trip_with_shifted_date, is_otp_error, plan_made_before, already_restored_the_shifted_date_plan) return restore_res