Skip to content

Instantly share code, notes, and snippets.

@mobileappconsultant
Last active December 7, 2018 15:01
Show Gist options
  • Save mobileappconsultant/fabd3aaa01337ee082de5249f526e523 to your computer and use it in GitHub Desktop.
Save mobileappconsultant/fabd3aaa01337ee082de5249f526e523 to your computer and use it in GitHub Desktop.
Python app to allow a user plan their journey from start to destination using Transport For London Live Service
#!/usr/bin/env python3
import requests
import re
import json
import sys
import time
##### IF YOU ARE USING COMMAND LINE TO RUN THIS PLS DO pip install requests re json sys time
# get current postcode from user -> DONE
# get destination postcode from user -> DONE
# check it meets the required format -> DONE
# complete the url with placeholders -> DONE
# submit request -> DONE
# process response check for errors -> DONE
# process response to display data -> DONE
tfl_provided_app_id = "99acf05e"
tfl_provided_app_key = "a5d066ef8faf90351c9cc1ff438648e3"
CONSTANT_REGULAR_EXPRESSION_FOR_POSTCODE = "([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\s?[0-9][A-Za-z]{2})"
CONSTANT_BASE_URL_FOR_TRANSPORT_FOR_LONDON = "https://api.tfl.gov.uk/journey/journeyresults/{}/to/{}?app_id={}&app_key={}"
CONSTANT_JOURNEYS = "journeys"
CONSTANT_DURATION = "duration"
CONSTANT_LEGS = "legs"
CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR = 60
CONSTANT_FARE = "fare"
CONSTANT_TOTAL_COST = "totalCost"
CONSTANT_FARES = "fares"
CONSTANT_INSTRUCTION = "instruction"
CONSTANT_DETAILED = "detailed"
CONSTANT_MODE = "mode"
CONSTANT_NAME = "name"
input_user_start_postcode = ""
input_user_end_postcode = ""
def getTravelInstructionsFromUser():
input_user_start_postcode = input("\nPlease enter the postcode of where you are starting your journey: ")
input_user_end_postcode = input("\nPlease enter the postcode of where you are going: ")
return [input_user_start_postcode, input_user_end_postcode]
def validateUserEnteredInstructions(travel_instructions):
print(travel_instructions[0])
print(travel_instructions[1])
return re.search(CONSTANT_REGULAR_EXPRESSION_FOR_POSTCODE, travel_instructions[0]) and re.search(
CONSTANT_REGULAR_EXPRESSION_FOR_POSTCODE,
travel_instructions[1])
def completeTheUrlForTheRequestToTheServer(travel_instructions):
return CONSTANT_BASE_URL_FOR_TRANSPORT_FOR_LONDON.format(travel_instructions[0], travel_instructions[1],
tfl_provided_app_id, tfl_provided_app_key).replace(" ", "")
def getTimeInMinutesAndSections(data, journeyIndex):
journeys = data[CONSTANT_JOURNEYS]
print(journeys[journeyIndex][CONSTANT_DURATION])
def getAverageTravelTime(data):
journeys = data[CONSTANT_JOURNEYS]
totalTime = 0
for journeyIndex in range(len(journeys)):
totalTime += journeys[journeyIndex][CONSTANT_DURATION]
averageTravelTime = abs(totalTime / len(journeys))
return (len(journeys), int(averageTravelTime / CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR),
int(averageTravelTime % CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR))
def displayTheTravelOptionsToTheUser(data):
computedAverageTimeTuple = getAverageTravelTime(data)
computedAverageArrivalTime = computeEstimatedArrivalTimeBasedOnAverageTime(computedAverageTimeTuple)
print(
"There are {} routes and {} TFL lines available to you when embarking on this journey and Average Travel Time across all {} routes is: {} hours and {} minutes."
"\nThe time now is {}:{} and your estimated arrival time is {}:{}".
format(len(data["journeys"]), len(data["lines"]), computedAverageTimeTuple[0],
computedAverageTimeTuple[1], computedAverageTimeTuple[2], computedAverageArrivalTime[0],
computedAverageArrivalTime[1], computedAverageArrivalTime[2], computedAverageArrivalTime[3]))
def retrieveJourneyWithShortestTravelDuration(data):
journeys = data[CONSTANT_JOURNEYS]
listOfDurations = {}
for journeyIndex in range(len(journeys)):
listOfDurations[journeyIndex] = [journeys[journeyIndex][CONSTANT_DURATION]]
return journeys[sorted(listOfDurations)[0]]
# to display a journey I need the index of the journey or just the journey data
# to display a journey I need the index of the journey or just the journey data
def displaySpecificJourneyDetails(data, journeyIndex=None):
""" if we pass index then get the journey by that index else use data as it is"""
singleJourneyWeAreInterestedIn = data
if journeyIndex != None:
journeys = data[CONSTANT_JOURNEYS]
singleJourneyWeAreInterestedIn = journeys[journeyIndex]
legsOfThisJourney = singleJourneyWeAreInterestedIn[CONSTANT_LEGS]
else:
legsOfThisJourney = data[CONSTANT_LEGS]
if singleJourneyWeAreInterestedIn.get(CONSTANT_FARE):
journeyFareInformation = "Travel Cost: £{}".format('{:,.2f}').format(
singleJourneyWeAreInterestedIn[CONSTANT_FARE][CONSTANT_TOTAL_COST] / 100)
else:
journeyFareInformation = "No Cost Information"
print("\n++++++++++++++++++++++++JOURNEY START++++++++++++++++++++++++++++++++++++++\n")
print("\nJourney Duration(Approx): {} --> {} \n".format(singleJourneyWeAreInterestedIn[CONSTANT_DURATION],
journeyFareInformation))
displayTheDetailedStepsForThisJourney(legsOfThisJourney)
print("\n++++++++++++++++++++++++JOURNEY END++++++++++++++++++++++++++++++++++++++\n")
def displayTheDetailedStepsForThisJourney(data):
for indexOfThisJourney in range(len(data)):
singleJourneyLeg = data[indexOfThisJourney]
informationToDisplay = "Duration: {} mins {} {} {} {}{}" \
.format(int(singleJourneyLeg[CONSTANT_DURATION]), "**** ",
singleJourneyLeg[CONSTANT_INSTRUCTION][
CONSTANT_DETAILED], "**** ",
(lambda string: string.upper())(singleJourneyLeg[CONSTANT_MODE][
CONSTANT_NAME])
, " **** ")
print("\n{}".format(informationToDisplay))
def displayEverthingJor(data):
print("\n\n")
journeys = data[CONSTANT_JOURNEYS]
for index in range(len(journeys)):
print(displaySpecificJourneyDetails(data, index))
def computeEstimatedArrivalTimeBasedOnAverageTime(computedAverageTimeTuple):
localtime = time.localtime(time.time())
hours = localtime.tm_hour
mins = localtime.tm_min
if (hours < 10):
formattedHours = "0{}".format(hours)
else:
formattedHours = "{}".format(hours)
if (mins < 10):
formattedMins = "0{}".format(mins)
else:
formattedMins = "{}".format(mins)
expectedArrivalTimeInHours = int(formattedHours) + computedAverageTimeTuple[1]
expectedArrivalTimeInMins = int(formattedMins) + computedAverageTimeTuple[2]
if (expectedArrivalTimeInMins > CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR):
expectedArrivalTimeInHours += expectedArrivalTimeInMins / CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR
expectedArrivalTimeInMins = expectedArrivalTimeInMins % CONSTANT_NUMBER_OF_MINUTES_IN_AN_HR
return (formattedHours, formattedMins, int(expectedArrivalTimeInHours), int(expectedArrivalTimeInMins))
def submitRequestToTFL(completeUrlForTFL):
try:
r = requests.get(completeUrlForTFL, 10)
data = json.loads(r.content.decode())
return data
except requests.exceptions.Timeout as requestTimedOutOrTookTooLong:
print(requestTimedOutOrTookTooLong)
sys.exit(1)
# Maybe set up for a retry, or continue in a retry loop
except requests.exceptions.TooManyRedirects as tooManyReDirects:
print(tooManyReDirects)
sys.exit(1)
# Omo! URL was bad try a different one
except requests.exceptions.RequestException as thatUrlYouSentWasDodgy:
print(thatUrlYouSentWasDodgy)
sys.exit(1)
if __name__ == '__main__':
while True:
user_input = []
user_input = getTravelInstructionsFromUser()
isUserEnteredDataInTheRightFormat = validateUserEnteredInstructions(user_input)
if isUserEnteredDataInTheRightFormat != None:
print("Ok! Retrieving a route for you..Please wait a few seconds!")
completeUrlForTFL = completeTheUrlForTheRequestToTheServer(user_input)
print(completeUrlForTFL)
data = submitRequestToTFL(completeUrlForTFL)
if (data != None and data.get(CONSTANT_JOURNEYS) != None):
displayTheTravelOptionsToTheUser(data)
# display quickest journey yes or no
# input == yes # print(displaySpecificJourneyDetails(retrieveJourneyWithShortestTravelDuration(data)))
# input == no displayEverythingJor
howToDisplayTheJourneyInformationRetrieved = input(
"\nDo you wish to see the quickest journey to your destiznation? 'Y' or 'N': ").lower()
if howToDisplayTheJourneyInformationRetrieved == "y":
displaySpecificJourneyDetails(retrieveJourneyWithShortestTravelDuration(data))
else:
displayEverthingJor(data)
else:
print("Sorry Couldn't find a viable route for you")
else:
print("Sorry there was a problem with the postcodes you entered!...Try again")
continueResponse = input("Do you wish to try finding a new journey? 'Y' or 'N' ").lower()
if (continueResponse == "y"):
continue
else:
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment