def display_trip_economics(self, trip): print (trip.user_id,"|",trip.id, ",", trip.plan_id, \ "|", trip.multimodal_summary, \ "|", DateTime_to_Text(trip.starttime), "to", DateTime_to_Text(trip.endtime), \ "| from ", pointRow_to_geoText(trip.origin), "--> to", pointRow_to_geoText(trip.destination), \ "| time:", DateTimeDelta_to_Text(trip.duration), "| cost:", round(trip.cost, 2), round_dict_values(trip.cost_by_mode, 2), \ "| cals:", int(round(trip.calories)), round_dict_values(trip.calories_by_mode, 0), \ "| emission:", int(round(trip.emission)), round_dict_values(trip.emission_by_mode, 0), \ "| comfort:", trip.comfort, "| distance: ", int(round(trip.distance)), round_dict_values(trip.distance_by_mode, 0) )
def display_trip_economics_csv(self, trip): print (trip.user_id,"|", trip.device_id,"|", trip.id, "|", trip.plan_id, \ "|", trip.multimodal_summary, \ "|", DateTime_to_Text(trip.starttime), "|", DateTime_to_Text(trip.endtime), \ "|", DateTimeDelta_to_Text(trip.duration), "|", round(trip.cost, 2), \ "|", int(round(trip.calories)), "|", round(trip.emission/1000.0, 1), \ "|", trip.comfort, "|", round(trip.distance/1000.0, 1), \ "||", pointRow_to_geoText(trip.origin), "|", pointRow_to_geoText(trip.destination),\ "||", 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) )
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 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 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